From a32deb7296d6052092601c5900e1f71c84382736 Mon Sep 17 00:00:00 2001 From: Daniel Neus Date: Mon, 2 May 2016 12:31:46 +0200 Subject: remove all uses of EMSA1_BSI --- src/lib/cert/cvc/cvc_self.cpp | 4 ++-- src/lib/cert/x509/x509_ca.cpp | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) (limited to 'src/lib/cert') diff --git a/src/lib/cert/cvc/cvc_self.cpp b/src/lib/cert/cvc/cvc_self.cpp index fdc66bbfd..fa59f4c6d 100644 --- a/src/lib/cert/cvc/cvc_self.cpp +++ b/src/lib/cert/cvc/cvc_self.cpp @@ -99,7 +99,7 @@ EAC1_1_CVC create_self_signed_cert(Private_Key const& key, ASN1_Chr chr(opt.car.value()); AlgorithmIdentifier sig_algo; - std::string padding_and_hash("EMSA1_BSI(" + opt.hash_alg + ")"); + std::string padding_and_hash("EMSA1(" + 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); @@ -126,7 +126,7 @@ EAC1_1_Req create_cvc_req(Private_Key const& key, throw Invalid_Argument("CVC_EAC::create_self_signed_cert(): unsupported key type"); } AlgorithmIdentifier sig_algo; - std::string padding_and_hash("EMSA1_BSI(" + hash_alg + ")"); + std::string padding_and_hash("EMSA1(" + hash_alg + ")"); sig_algo.oid = OIDS::lookup(priv_key->algo_name() + "/" + padding_and_hash); sig_algo = AlgorithmIdentifier(sig_algo.oid, AlgorithmIdentifier::USE_NULL_PARAM); diff --git a/src/lib/cert/x509/x509_ca.cpp b/src/lib/cert/x509/x509_ca.cpp index 46c8c65f2..147fdd6ad 100644 --- a/src/lib/cert/x509/x509_ca.cpp +++ b/src/lib/cert/x509/x509_ca.cpp @@ -234,10 +234,8 @@ PK_Signer* choose_sig_format(const Private_Key& key, std::string padding; if(algo_name == "RSA") padding = "EMSA3"; - else if(algo_name == "DSA") + else if(algo_name == "DSA" || algo_name == "ECDSA" ) padding = "EMSA1"; - else if(algo_name == "ECDSA") - padding = "EMSA1_BSI"; else throw Invalid_Argument("Unknown X.509 signing key type: " + algo_name); -- cgit v1.2.3 From 705e67edda2332370686df138d17bca3fe682ed3 Mon Sep 17 00:00:00 2001 From: Daniel Neus Date: Wed, 18 May 2016 10:51:32 +0200 Subject: remove the already bitrotting and probably broken CVC implementation --- src/lib/cert/cvc/asn1_eac_str.cpp | 127 -------- src/lib/cert/cvc/asn1_eac_tm.cpp | 297 ------------------ src/lib/cert/cvc/cvc_ado.cpp | 127 -------- src/lib/cert/cvc/cvc_ado.h | 98 ------ src/lib/cert/cvc/cvc_cert.cpp | 135 --------- src/lib/cert/cvc/cvc_cert.h | 116 -------- src/lib/cert/cvc/cvc_gen_cert.h | 180 ----------- src/lib/cert/cvc/cvc_req.cpp | 53 ---- src/lib/cert/cvc/cvc_req.h | 59 ---- src/lib/cert/cvc/cvc_self.cpp | 339 --------------------- src/lib/cert/cvc/cvc_self.h | 180 ----------- src/lib/cert/cvc/eac_asn_obj.h | 239 --------------- src/lib/cert/cvc/eac_obj.h | 55 ---- src/lib/cert/cvc/ecdsa_sig.cpp | 59 ---- src/lib/cert/cvc/ecdsa_sig.h | 61 ---- src/lib/cert/cvc/info.txt | 35 --- src/lib/cert/cvc/signed_obj.cpp | 95 ------ src/lib/cert/cvc/signed_obj.h | 95 ------ src/tests/test_cvc.cpp | 611 -------------------------------------- 19 files changed, 2961 deletions(-) delete mode 100644 src/lib/cert/cvc/asn1_eac_str.cpp delete mode 100644 src/lib/cert/cvc/asn1_eac_tm.cpp delete mode 100644 src/lib/cert/cvc/cvc_ado.cpp delete mode 100644 src/lib/cert/cvc/cvc_ado.h delete mode 100644 src/lib/cert/cvc/cvc_cert.cpp delete mode 100644 src/lib/cert/cvc/cvc_cert.h delete mode 100644 src/lib/cert/cvc/cvc_gen_cert.h delete mode 100644 src/lib/cert/cvc/cvc_req.cpp delete mode 100644 src/lib/cert/cvc/cvc_req.h delete mode 100644 src/lib/cert/cvc/cvc_self.cpp delete mode 100644 src/lib/cert/cvc/cvc_self.h delete mode 100644 src/lib/cert/cvc/eac_asn_obj.h delete mode 100644 src/lib/cert/cvc/eac_obj.h delete mode 100644 src/lib/cert/cvc/ecdsa_sig.cpp delete mode 100644 src/lib/cert/cvc/ecdsa_sig.h delete mode 100644 src/lib/cert/cvc/info.txt delete mode 100644 src/lib/cert/cvc/signed_obj.cpp delete mode 100644 src/lib/cert/cvc/signed_obj.h delete mode 100644 src/tests/test_cvc.cpp (limited to 'src/lib/cert') diff --git a/src/lib/cert/cvc/asn1_eac_str.cpp b/src/lib/cert/cvc/asn1_eac_str.cpp deleted file mode 100644 index 72ad24926..000000000 --- a/src/lib/cert/cvc/asn1_eac_str.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/* -* Simple ASN.1 String Types -* (C) 2007 FlexSecure GmbH -* 2008-2011 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include -#include -#include -#include -#include -#include -#include - -namespace Botan { - -/* -* Create an ASN1_EAC_String -*/ -ASN1_EAC_String::ASN1_EAC_String(const std::string& str, ASN1_Tag t) : m_tag(t) - { - m_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 m_iso_8859_str; - } - -/* -* Return this string in local encoding -*/ -std::string ASN1_EAC_String::value() const - { - return Charset::transcode(m_iso_8859_str, LATIN1_CHARSET, LOCAL_CHARSET); - } - -/* -* Return the type of this string object -*/ -ASN1_Tag ASN1_EAC_String::tagging() const - { - return m_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 != m_tag) - { - std::stringstream ss; - - ss << "ASN1_EAC_String tag mismatch, tag was " - << std::hex << obj.type_tag - << " expected " - << std::hex << m_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), LOCAL_CHARSET, charset_is), - 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(m_iso_8859_str.data()); - const size_t rep_len = m_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 deleted file mode 100644 index 9c65fcf6a..000000000 --- a/src/lib/cert/cvc/asn1_eac_tm.cpp +++ /dev/null @@ -1,297 +0,0 @@ -/* -* EAC Time Types -* (C) 2007 FlexSecure GmbH -* 2008-2009 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Botan { - -namespace { - -std::vector enc_two_digit(u32bit in) - { - std::vector result; - in %= 100; - if(in < 10) - result.push_back(0x00); - else - { - u32bit y_first_pos = round_down(in, 10) / 10; - result.push_back(static_cast(y_first_pos)); - } - - u32bit y_sec_pos = in % 10; - result.push_back(static_cast(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) : m_tag(t) - { - calendar_point cal = calendar_value(time); - - m_year = cal.year; - m_month = cal.month; - m_day = cal.day; - } - -/* -* Create an EAC_Time -*/ -EAC_Time::EAC_Time(const std::string& t_spec, ASN1_Tag t) : m_tag(t) - { - set_to(t_spec); - } - -/* -* Create an EAC_Time -*/ -EAC_Time::EAC_Time(u32bit y, u32bit m, u32bit d, ASN1_Tag t) : - m_year(y), m_month(m), m_day(d), m_tag(t) - { - } - -/* -* Set the time with a human readable string -*/ -void EAC_Time::set_to(const std::string& time_str) - { - if(time_str == "") - { - m_year = m_month = m_day = 0; - return; - } - - std::vector 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); - - m_year = to_u32bit(params[0]); - m_month = to_u32bit(params[1]); - m_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(m_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(m_year * 10000 + m_month * 100 + m_day); - } - -/* -* Return if the time has been set somehow -*/ -bool EAC_Time::time_is_set() const - { - return (m_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"); - - // desired format: "%04d/%02d/%02d" - std::stringstream output; - output << std::setfill('0') - << std::setw(4) << m_year << "/" - << std::setw(2) << m_month << "/" - << std::setw(2) << m_day; - return output.str(); - } - -/* -* Do a general sanity check on the time -*/ -bool EAC_Time::passes_sanity_check() const - { - if(m_year < 2000 || m_year > 2099) - return false; - if(m_month == 0 || m_month > 12) - return false; - if(m_day == 0 || m_day > 31) - return false; - - return true; - } - -/* -* modification functions -*/ -void EAC_Time::add_years(u32bit years) - { - m_year += years; - } - -void EAC_Time::add_months(u32bit months) - { - m_year += months/12; - m_month += months % 12; - if(m_month > 12) - { - m_year += 1; - m_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(m_year < other.m_year) return EARLIER; - if(m_year > other.m_year) return LATER; - if(m_month < other.m_month) return EARLIER; - if(m_month > other.m_month) return LATER; - if(m_day < other.m_day) return EARLIER; - if(m_day > other.m_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 != m_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]); - m_year = tmp_year + 2000; - m_month = tmp_mon; - m_day = tmp_day; - } - catch (Invalid_Argument) - { - throw Decoding_Error("EAC_Time decoding failed"); - } - - } - -/* -* make the value an octet string for encoding -*/ -std::vector EAC_Time::encoded_eac_time() const - { - std::vector result; - result += enc_two_digit(m_year); - result += enc_two_digit(m_month); - result += enc_two_digit(m_day); - return result; - } - -} diff --git a/src/lib/cert/cvc/cvc_ado.cpp b/src/lib/cert/cvc/cvc_ado.cpp deleted file mode 100644 index f803c6bf3..000000000 --- a/src/lib/cert/cvc/cvc_ado.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/* -* CVC Certificate Constructor -* (C) 2007 FlexSecure GmbH -* 2008 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include -#include - -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 inner_cert; - BER_Decoder(m_tbs_bits) - .start_cons(ASN1_Tag(33)) - .raw_bytes(inner_cert) - .end_cons() - .decode(m_car) - .verify_end(); - - std::vector 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); - m_sig_algo = m_req.m_sig_algo; - } - -std::vector EAC1_1_ADO::make_signed(PK_Signer& signer, - const std::vector& tbs_bits, - RandomNumberGenerator& rng) - { - const std::vector 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 & res_tbs_bits, - ECDSA_Signature & res_sig) - { - std::vector concat_sig; - std::vector 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 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::m_sig.get_concatenation(); - - out.write(DER_Encoder() - .start_cons(ASN1_Tag(7), APPLICATION) - .raw_bytes(m_tbs_bits) - .encode(concat_sig, OCTET_STRING, ASN1_Tag(55), APPLICATION) - .end_cons() - .get_contents()); - } - -std::vector EAC1_1_ADO::tbs_data() const - { - return m_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 deleted file mode 100644 index 4b861ec81..000000000 --- a/src/lib/cert/cvc/cvc_ado.h +++ /dev/null @@ -1,98 +0,0 @@ -/* -* EAC1_1 CVC ADO -* (C) 2008 Falko Strenzke -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_EAC_CVC_ADO_H__ -#define BOTAN_EAC_CVC_ADO_H__ - -#include -#include -#include -#include - -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 - { - public: - friend class EAC1_1_obj; - - /** - * 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 make_signed( - PK_Signer& signer, - const std::vector& 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 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 & 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 deleted file mode 100644 index 280a8acda..000000000 --- a/src/lib/cert/cvc/cvc_cert.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/* -* (C) 2007 FlexSecure GmbH -* 2008-2010 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include -#include - -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 enc_pk; - std::vector enc_chat_val; - size_t cpi; - BER_Decoder tbs_cert(m_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, m_sig_algo); - - m_chat_val = enc_chat_val[0]; - - m_self_signed = (m_car.iso_8859() == m_chr.iso_8859()); - } - -/* -* CVC Certificate Constructor -*/ -EAC1_1_CVC::EAC1_1_CVC(DataSource& in) - { - init(in); - m_self_signed = false; - do_decode(); - } - -EAC1_1_CVC::EAC1_1_CVC(const std::string& in) - { - DataSource_Stream stream(in, true); - init(stream); - m_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&, - AlgorithmIdentifier&) - { - throw Internal_Error("decode_eac1_1_key: Unimplemented"); - return 0; - } - -EAC1_1_CVC make_cvc_cert(PK_Signer& signer, - const std::vector& 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 enc_chat_val; - enc_chat_val.push_back(holder_auth_templ); - - std::vector enc_cpi; - enc_cpi.push_back(0x00); - std::vector 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 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 deleted file mode 100644 index a45388550..000000000 --- a/src/lib/cert/cvc/cvc_cert.h +++ /dev/null @@ -1,116 +0,0 @@ -/* -* EAC1_1 CVC -* (C) 2008 Falko Strenzke -* 2008 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_CVC_EAC_H__ -#define BOTAN_CVC_EAC_H__ - -#include -#include -#include - -namespace Botan { - -/** -* This class represents TR03110 (EAC) v1.1 CV Certificates -*/ -class BOTAN_DLL EAC1_1_CVC : public EAC1_1_gen_CVC//Signed_Object - { - public: - friend class EAC1_1_obj; - - /** - * 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& 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& 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 deleted file mode 100644 index 2c3bca73d..000000000 --- a/src/lib/cert/cvc/cvc_gen_cert.h +++ /dev/null @@ -1,180 +0,0 @@ -/* -* EAC1_1 general CVC -* (C) 2008 Falko Strenzke -* 2008-2010 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_EAC_CVC_GEN_CERT_H__ -#define BOTAN_EAC_CVC_GEN_CERT_H__ - -#include -#include -#include -#include - -namespace Botan { - -/** -* This class represents TR03110 (EAC) v1.1 generalized CV Certificates -*/ -template -class EAC1_1_gen_CVC : public EAC1_1_obj // CRTP continuation from EAC1_1_obj - { - friend class EAC1_1_obj; - - 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 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 build_cert_body(const std::vector& 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 make_signed( - PK_Signer& signer, - const std::vector& tbs_bits, - RandomNumberGenerator& rng); - - EAC1_1_gen_CVC() { m_pk = nullptr; } - - virtual ~EAC1_1_gen_CVC() - { delete m_pk; } - - protected: - ECDSA_PublicKey* m_pk; - ASN1_Chr m_chr; - bool m_self_signed; - - static void decode_info(DataSource& source, - std::vector & res_tbs_bits, - ECDSA_Signature & res_sig); - - }; - -template ASN1_Chr EAC1_1_gen_CVC::get_chr() const - { - return m_chr; - } - -template bool EAC1_1_gen_CVC::is_self_signed() const - { - return m_self_signed; - } - -template -std::vector EAC1_1_gen_CVC::make_signed( - PK_Signer& signer, - const std::vector& 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 -Public_Key* EAC1_1_gen_CVC::subject_public_key() const - { - return new ECDSA_PublicKey(*m_pk); - } - -template std::vector EAC1_1_gen_CVC::build_cert_body(const std::vector& tbs) - { - return DER_Encoder() - .start_cons(ASN1_Tag(78), APPLICATION) - .raw_bytes(tbs) - .end_cons().get_contents_unlocked(); - } - -template std::vector EAC1_1_gen_CVC::tbs_data() const - { - return build_cert_body(EAC1_1_obj::m_tbs_bits); - } - -template void EAC1_1_gen_CVC::encode(Pipe& out, X509_Encoding encoding) const - { - std::vector concat_sig(EAC1_1_obj::m_sig.get_concatenation()); - std::vector der = DER_Encoder() - .start_cons(ASN1_Tag(33), APPLICATION) - .start_cons(ASN1_Tag(78), APPLICATION) - .raw_bytes(EAC1_1_obj::m_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 -void EAC1_1_gen_CVC::decode_info( - DataSource& source, - std::vector & res_tbs_bits, - ECDSA_Signature & res_sig) - { - std::vector 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 deleted file mode 100644 index 1cb6b50ac..000000000 --- a/src/lib/cert/cvc/cvc_req.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* -* (C) 2007 FlexSecure GmbH -* 2008-2010 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include -#include -#include - -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 enc_pk; - BER_Decoder tbs_cert(m_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, m_sig_algo); - } - -EAC1_1_Req::EAC1_1_Req(DataSource& in) - { - init(in); - m_self_signed = true; - do_decode(); - } - -EAC1_1_Req::EAC1_1_Req(const std::string& in) - { - DataSource_Stream stream(in, true); - init(stream); - m_self_signed = true; - do_decode(); - } - -} diff --git a/src/lib/cert/cvc/cvc_req.h b/src/lib/cert/cvc/cvc_req.h deleted file mode 100644 index b71a8e764..000000000 --- a/src/lib/cert/cvc/cvc_req.h +++ /dev/null @@ -1,59 +0,0 @@ -/* -* EAC1_1 CVC Request -* (C) 2008 Falko Strenzke -* 2010 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_EAC_CVC_REQ_H__ -#define BOTAN_EAC_CVC_REQ_H__ - -#include - -namespace Botan { - -/** -* This class represents TR03110 v1.1 EAC CV Certificate Requests. -*/ -class BOTAN_DLL EAC1_1_Req : public EAC1_1_gen_CVC - { - public: - friend class EAC1_1_ADO; - friend class EAC1_1_obj; - - /** - * 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 deleted file mode 100644 index fa59f4c6d..000000000 --- a/src/lib/cert/cvc/cvc_self.cpp +++ /dev/null @@ -1,339 +0,0 @@ -/* -* (C) 2007 FlexSecure GmbH -* 2008-2010 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include -#include -#include -#include -#include - -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 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(&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(" + 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 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(&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(" + 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 enc_public_key = eac_1_1_encoding(priv_key, sig_algo.oid); - - std::vector enc_cpi; - enc_cpi.push_back(0x00); - std::vector tbs = DER_Encoder() - .encode(enc_cpi, OCTET_STRING, ASN1_Tag(41), APPLICATION) - .raw_bytes(enc_public_key) - .encode(chr) - .get_contents_unlocked(); - - std::vector signed_cert = - EAC1_1_gen_CVC::make_signed(signer, - EAC1_1_gen_CVC::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(&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 tbs_bits = req.BER_encode(); - tbs_bits += DER_Encoder().encode(car).get_contents(); - - std::vector 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(&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(&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(&ced) > *static_cast(&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 pk(signee.subject_public_key()); - ECDSA_PublicKey* subj_pk = dynamic_cast(pk.get()); - subj_pk->set_parameter_encoding(EC_DOMPAR_ENC_EXPLICIT); - - std::vector 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(&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 pk(signee.subject_public_key()); - ECDSA_PublicKey* subj_pk = dynamic_cast(pk.get()); - std::unique_ptr 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 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(&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 deleted file mode 100644 index d56e96c40..000000000 --- a/src/lib/cert/cvc/cvc_self.h +++ /dev/null @@ -1,180 +0,0 @@ -/* -* CVC Self-Signed Certificate -* (C) 2007 FlexSecure GmbH -* 2008 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_CVC_EAC_SELF_H__ -#define BOTAN_CVC_EAC_SELF_H__ - -#include -#include -#include -#include -#include - -namespace Botan { - -/** -* This class represents a set of options used for the creation of CVC certificates -*/ -class BOTAN_DLL EAC1_1_CVC_Options - { - public: - - // public member variable: - ASN1_Car car; - - // public member variable: - ASN1_Chr chr; - - // public member variable: - byte holder_auth_templ; - - // public member variable: - ASN1_Ced ced; - - // public member variable: - ASN1_Cex cex; - - // public member variable: - 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 deleted file mode 100644 index b4dcb6342..000000000 --- a/src/lib/cert/cvc/eac_asn_obj.h +++ /dev/null @@ -1,239 +0,0 @@ -/* -* EAC ASN.1 Objects -* (C) 2007-2008 FlexSecure GmbH -* 2008-2010 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_EAC_ASN1_OBJ_H__ -#define BOTAN_EAC_ASN1_OBJ_H__ - -#include -#include - -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 m_year; } - - /** - * Get the month value of this objects. - * @return month value - */ - u32bit get_month() const { return m_month; } - - /** - * Get the day value of this objects. - * @return day value - */ - u32bit get_day() const { return m_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 encoded_eac_time() const; - bool passes_sanity_check() const; - u32bit m_year, m_month, m_day; - ASN1_Tag m_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 m_iso_8859_str; - ASN1_Tag m_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 deleted file mode 100644 index a6e676076..000000000 --- a/src/lib/cert/cvc/eac_obj.h +++ /dev/null @@ -1,55 +0,0 @@ -/* -* EAC1_1 objects -* (C) 2008 Falko Strenzke -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_EAC_OBJ_H__ -#define BOTAN_EAC_OBJ_H__ - -#include -#include - -namespace Botan { - -/** -* TR03110 v1.1 EAC CV Certificate -*/ -template // 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 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, m_tbs_bits, m_sig); - } - catch(Decoding_Error) - { - throw Decoding_Error(m_PEM_label_pref + " decoding failed"); - } - } - - virtual ~EAC1_1_obj(){} - }; - -} - -#endif diff --git a/src/lib/cert/cvc/ecdsa_sig.cpp b/src/lib/cert/cvc/ecdsa_sig.cpp deleted file mode 100644 index 5e85d7932..000000000 --- a/src/lib/cert/cvc/ecdsa_sig.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* -* ECDSA Signature -* (C) 2007 Falko Strenzke, FlexSecure GmbH -* (C) 2008-2010 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include - -namespace Botan { - -ECDSA_Signature::ECDSA_Signature(const std::vector& ber) - { - BER_Decoder(ber) - .start_cons(SEQUENCE) - .decode(m_r) - .decode(m_s) - .end_cons() - .verify_end(); - } - -std::vector ECDSA_Signature::DER_encode() const - { - return DER_Encoder() - .start_cons(SEQUENCE) - .encode(get_r()) - .encode(get_s()) - .end_cons() - .get_contents_unlocked(); - } - -std::vector 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 result(sv_r); - result += sv_s; - return unlock(result); - } - -ECDSA_Signature decode_concatenation(const std::vector& 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 deleted file mode 100644 index 2845cbec9..000000000 --- a/src/lib/cert/cvc/ecdsa_sig.h +++ /dev/null @@ -1,61 +0,0 @@ -/* -* ECDSA Signature -* (C) 2007 Falko Strenzke, FlexSecure GmbH -* (C) 2008-2010 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_ECDSA_SIGNATURE_H__ -#define BOTAN_ECDSA_SIGNATURE_H__ - -#include -#include -#include - -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& ber); - - const BigInt& get_r() const { return m_r; } - const BigInt& get_s() const { return m_s; } - - /** - * return the r||s - */ - std::vector get_concatenation() const; - - std::vector 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& concatenation); - -} - -#endif diff --git a/src/lib/cert/cvc/info.txt b/src/lib/cert/cvc/info.txt deleted file mode 100644 index e3da5435e..000000000 --- a/src/lib/cert/cvc/info.txt +++ /dev/null @@ -1,35 +0,0 @@ -define CARD_VERIFIABLE_CERTIFICATES 20131128 -load_on request - - -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 - - - -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 - - - -asn1 -bigint -ecdsa -filters -oid_lookup -pem -pubkey - diff --git a/src/lib/cert/cvc/signed_obj.cpp b/src/lib/cert/cvc/signed_obj.cpp deleted file mode 100644 index 1e3849663..000000000 --- a/src/lib/cert/cvc/signed_obj.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* -* EAC SIGNED Object -* (C) 1999-2010 Jack Lloyd -* 2007 FlexSecure GmbH -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include -#include -#include - -namespace Botan { - -/* -* Return a BER encoded X.509 object -*/ -std::vector 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 m_sig_algo; - } - -bool EAC_Signed_Object::check_signature(Public_Key& pub_key, - const std::vector& sig) const - { - try - { - std::vector sig_info = - split_on(OIDS::lookup(m_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 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(m_PEM_label_pref + " decoding failed (" + what + ")"); - } - catch(Invalid_Argument& e) - { - const std::string what = e.what(); - throw Decoding_Error(m_PEM_label_pref + " decoding failed (" + what + ")"); - } - } - -} diff --git a/src/lib/cert/cvc/signed_obj.h b/src/lib/cert/cvc/signed_obj.h deleted file mode 100644 index b3fe20f31..000000000 --- a/src/lib/cert/cvc/signed_obj.h +++ /dev/null @@ -1,95 +0,0 @@ -/* -* EAC SIGNED Object -* (C) 2007 FlexSecure GmbH -* 2008 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_EAC_SIGNED_OBJECT_H__ -#define BOTAN_EAC_SIGNED_OBJECT_H__ - -#include -#include -#include -#include - -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 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 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& 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 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 m_sig_algo; - std::vector m_tbs_bits; - std::string m_PEM_label_pref; - std::vector m_PEM_labels_allowed; - private: - virtual void force_decode() = 0; - }; - -} - -#endif diff --git a/src/tests/test_cvc.cpp b/src/tests/test_cvc.cpp deleted file mode 100644 index dc4b50ebd..000000000 --- a/src/tests/test_cvc.cpp +++ /dev/null @@ -1,611 +0,0 @@ -/* -* CVC EAC1.1 tests -* -* (C) 2008 Falko Strenzke (strenzke@flexsecure.de) -* 2008,2015 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include "tests.h" - -#if defined(BOTAN_HAS_CARD_VERIFIABLE_CERTIFICATES) - -#include -#include -#include -#include -#include -#include -#include - -#endif - -namespace Botan_Tests { - -namespace { - -#if defined(BOTAN_HAS_CARD_VERIFIABLE_CERTIFICATES) - -using namespace Botan; - -// helper functions -void helper_write_file(EAC_Signed_Object const& to_write, const std::string& 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(const std::string& file_path1, const std::string& 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; - } - -Test::Result test_cvc_times() - { - Test::Result result("CVC"); - - auto time1 = Botan::EAC_Time("2008-02-01"); - auto time2 = Botan::EAC_Time("2008/02/28"); - auto time3 = Botan::EAC_Time("2004-06-14"); - - result.confirm("time1 set", time1.time_is_set()); - result.confirm("time2 set", time2.time_is_set()); - result.confirm("time3 set", time3.time_is_set()); - - result.test_eq("time1 readable_string", time1.readable_string(), "2008/02/01"); - result.test_eq("time2 readable_string", time2.readable_string(), "2008/02/28"); - result.test_eq("time3 readable_string", time3.readable_string(), "2004/06/14"); - - result.test_eq("not set", Botan::EAC_Time("").time_is_set(), false); - - const std::vector invalid = { - " ", - "2008`02-01", - "9999-02-01", - "2000-02-01 17", - "999921" - }; - - for(auto&& v : invalid) - { - result.test_throws("invalid time " + v, [v]() { Botan::EAC_Time w(v); }); - } - - return result; - } - -Test::Result test_enc_gen_selfsigned() - { - Test::Result result("CVC"); - - 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(Test::rng(), dom_pars); - key.set_parameter_encoding(EC_DOMPAR_ENC_IMPLICITCA); - EAC1_1_CVC cert = CVC_EAC::create_self_signed_cert(key, opts, Test::rng()); - - std::vector der(cert.BER_encode()); - std::ofstream cert_file; - cert_file.open(Test::data_file("ecc/my_cv_cert.ber"), std::ios::binary); - cert_file.write((char*)der.data(), der.size()); - cert_file.close(); - - EAC1_1_CVC cert_in(Test::data_file("ecc/my_cv_cert.ber")); - result.confirm("reloaded cert matches", cert_in == cert); - - // encoding it again while it has no dp - std::vector der2(cert_in.BER_encode()); - std::ofstream cert_file2(Test::data_file("ecc/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(Test::data_file("ecc/my_cv_cert.ber")); - std::ifstream cert_2_in(Test::data_file("ecc/my_cv_cert2.ber")); - std::vector sv1; - std::vector sv2; - if (!cert_1_in || cert_2_in) - { - result.test_failure("Unable to reread cert 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); - } - - result.test_gte("size", sv1.size(), 10); - result.test_ne("reencoded file of cert without domain parameters is different from original", sv1, sv2); - - result.test_eq("car", cert_in.get_car().value(), "my_opt_car"); - result.test_eq("chr", cert_in.get_chr().value(), "my_opt_car"); - result.test_eq("ced", cert_in.get_ced().as_string(), "20100727"); - result.test_eq("ced", cert_in.get_ced().readable_string(), "2010/07/27"); - - try - { - ASN1_Ced invalid("1999 01 01"); - result.test_failure("Allowed creation of invalid 1999 ASN1_Ced"); - } - catch(...) {} - - try - { - ASN1_Ced("2100 01 01"); - result.test_failure("Allowed creation of invalid 2100 ASN1_Ced"); - } - catch(...) {} - - 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): - - result.confirm("implicit CA", p_ecdsa_pk->domain_format() == EC_DOMPAR_ENC_IMPLICITCA); - - try - { - const BigInt order = p_ecdsa_pk->domain().get_order(); - result.test_failure("Expected accessing domain to fail"); - } - catch (Invalid_State) {} - { - } - - // 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); - result.test_eq("order", p_ecdsa_pk2->domain().get_order(), dom_pars.get_order()); - result.confirm("verified signature", cert_in.check_signature(*p_pk2)); - - return result; - } - -Test::Result test_enc_gen_req() - { - Test::Result result("CVC"); - - 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(Test::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, Test::rng()); - std::vector der(req.BER_encode()); - std::ofstream req_file(Test::data_file("ecc/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(Test::data_file("ecc/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); - result.test_eq("order", p_ecdsa_pk->domain().get_order(), dom_pars.get_order()); - result.confirm("signature valid on CVC request", req_in.check_signature(*p_pk)); - - return result; - } - -Test::Result test_cvc_req_ext() - { - EAC1_1_Req req_in(Test::data_file("ecc/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()); - - Test::Result result("CVC"); - result.test_eq("order", p_ecdsa_pk->domain().get_order(), dom_pars.get_order()); - result.confirm("signature valid on CVC request", req_in.check_signature(*p_pk)); - return result; - } - -Test::Result test_cvc_ado_creation() - { - Test::Result result("CVC"); - - 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")); - ECDSA_PrivateKey req_key(Test::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, Test::rng()); - std::vector der(req.BER_encode()); - std::ofstream req_file(Test::data_file("ecc/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(Test::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, Test::rng()); - result.confirm("ADO signature verifies", ado.check_signature(ado_key)); - - std::ofstream ado_file(Test::data_file("ecc/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(Test::data_file("ecc/ado")); - result.confirm("ADOs match", ado == ado2); - - result.confirm("ADO signature valid", ado2.check_signature(ado_key)); - - return result; - } - -Test::Result test_cvc_ado_comparison() - { - Test::Result result("CVC"); - - 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(Test::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, Test::rng()); - - - // create an ado with that req - ECDSA_PrivateKey ado_key(Test::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, Test::rng()); - result.confirm("ADO signature valid", ado.check_signature(ado_key)); - // 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(Test::rng(), dom_pars); - req_key.set_parameter_encoding(EC_DOMPAR_ENC_IMPLICITCA); - //EAC1_1_Req req2 = CVC_EAC::create_cvc_req(req_key2, opts2, Test::rng()); - EAC1_1_Req req2 = CVC_EAC::create_cvc_req(req_key2, opts2.chr, opts2.hash_alg, Test::rng()); - ECDSA_PrivateKey ado_key2(Test::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, Test::rng()); - result.confirm("ADO signature after creation", ado2.check_signature(ado_key2)); - - result.confirm("ADOs should not be equal", ado != ado2); - // std::ofstream ado_file(Test::data_file("ecc/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(Test::data_file("ecc/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"); - - return result; - } - -void confirm_cex_time(Test::Result& result, - const ASN1_Cex& cex, - size_t exp_year, - size_t exp_month) - { - result.test_eq("year", cex.get_year(), exp_year); - result.test_eq("month", cex.get_month(), exp_month); - } - -Test::Result test_eac_time() - { - Test::Result result("CVC"); - - EAC_Time sooner("", ASN1_Tag(99)); - sooner.set_to("2007 12 12"); - EAC_Time later("2007 12 13"); - - result.confirm("sooner < later", sooner < later); - result.confirm("self-equal", sooner == sooner); - - ASN1_Cex my_cex("2007 08 01"); - my_cex.add_months(12); - confirm_cex_time(result, my_cex, 2008, 8); - - my_cex.add_months(4); - confirm_cex_time(result, my_cex, 2008, 12); - - my_cex.add_months(4); - confirm_cex_time(result, my_cex, 2009, 4); - - my_cex.add_months(41); - confirm_cex_time(result, my_cex, 2012, 9); - - return result; - } - -Test::Result test_ver_cvca() - { - Test::Result result("CVC"); - - EAC1_1_CVC cvc(Test::data_file("ecc/cvca01.cv.crt")); - - std::unique_ptr p_pk2(cvc.subject_public_key()); - result.confirm("verified CVCA cert", cvc.check_signature(*p_pk2)); - - try - { - ECDSA_PublicKey* p_ecdsa_pk2 = dynamic_cast(p_pk2.get()); - p_ecdsa_pk2->domain().get_order(); - result.test_failure("Expected failure"); - } - catch(Invalid_State) - { - result.test_note("Accessing order failed"); - } - - return result; - } - -Test::Result test_copy_and_assignment() - { - Test::Result result("CVC"); - - EAC1_1_CVC cert_in(Test::data_file("ecc/cvca01.cv.crt")); - EAC1_1_CVC cert_cp(cert_in); - EAC1_1_CVC cert_ass = cert_in; - - result.confirm("same cert", cert_in == cert_cp); - result.confirm("same cert", cert_in == cert_ass); - - EAC1_1_ADO ado_in(Test::data_file("ecc/ado.cvcreq")); - EAC1_1_ADO ado_cp(ado_in); - EAC1_1_ADO ado_ass = ado_in; - result.confirm("same", ado_in == ado_cp); - result.confirm("same", ado_in == ado_ass); - - EAC1_1_Req req_in(Test::data_file("ecc/DE1_flen_chars_cvcRequest_ECDSA.der")); - EAC1_1_Req req_cp(req_in); - EAC1_1_Req req_ass = req_in; - result.confirm("same", req_in == req_cp); - result.confirm("same", req_in == req_ass); - - return result; - } - -Test::Result test_eac_str_illegal_values() - { - Test::Result result("CVC"); - - try - { - EAC1_1_CVC(Test::data_file("ecc/cvca_illegal_chars.cv.crt")); - result.test_failure("Accepted invalid EAC 1.1 CVC"); - } - catch (Decoding_Error) {} - - try - { - EAC1_1_CVC(Test::data_file("ecc/cvca_illegal_chars2.cv.crt")); - result.test_failure("Accepted invalid EAC 1.1 CVC #2"); - } - catch (Decoding_Error) {} - - return result; - } - -Test::Result test_tmp_eac_str_enc() - { - Test::Result result("CVC"); - try - { - ASN1_Car("abc!+-µ\n"); - result.test_failure("Accepted invalid EAC string"); - } - catch(Invalid_Argument) {} - - return result; - } - -Test::Result test_cvc_chain() - { - Test::Result result("CVC"); - - EC_Group dom_pars(OID("1.3.36.3.3.2.8.1.1.5")); // "german curve" - ECDSA_PrivateKey cvca_privk(Test::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, Test::rng()); - std::ofstream cvca_file(Test::data_file("ecc/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(Test::rng(), dom_pars); - ASN1_Car car2("DECVCA00002"); - EAC1_1_CVC cvca_cert2 = DE_EAC::create_cvca(cvca_privk2, hash, car2, true, true, 12, Test::rng()); - EAC1_1_CVC link12 = DE_EAC::link_cvca(cvca_cert, cvca_privk, cvca_cert2, Test::rng()); - std::vector link12_sv = link12.BER_encode(); - std::ofstream link12_file(Test::data_file("ecc/cvc_chain_link12.cer"), std::ios::binary); - link12_file.write((char*)link12_sv.data(), link12_sv.size()); - link12_file.close(); - - // verify the link - result.confirm("signature valid", link12.check_signature(cvca_privk)); - EAC1_1_CVC link12_reloaded(Test::data_file("ecc/cvc_chain_link12.cer")); - EAC1_1_CVC cvca1_reloaded(Test::data_file("ecc/cvc_chain_cvca.cer")); - std::unique_ptr cvca1_rel_pk(cvca1_reloaded.subject_public_key()); - result.confirm("signature valid", link12_reloaded.check_signature(*cvca1_rel_pk)); - - // create first round dvca-req - ECDSA_PrivateKey dvca_priv_key(Test::rng(), dom_pars); - EAC1_1_Req dvca_req = DE_EAC::create_cvc_req(dvca_priv_key, ASN1_Chr("DEDVCAEPASS"), hash, Test::rng()); - std::ofstream dvca_file(Test::data_file("ecc/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, Test::rng()); - result.test_eq("DVCA car", dvca_cert1.get_car().iso_8859(), "DECVCA00001"); - result.test_eq("DVCA chr", dvca_cert1.get_chr().iso_8859(), "DEDVCAEPASS00001"); - helper_write_file(dvca_cert1, Test::data_file("ecc/cvc_chain_dvca_cert1.cer")); - - // make a second round dvca ado request - ECDSA_PrivateKey dvca_priv_key2(Test::rng(), dom_pars); - EAC1_1_Req dvca_req2 = DE_EAC::create_cvc_req(dvca_priv_key2, ASN1_Chr("DEDVCAEPASS"), hash, Test::rng()); - std::ofstream dvca_file2(Test::data_file("ecc/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()), Test::rng()); - helper_write_file(dvca_ado2, Test::data_file("ecc/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); - EAC1_1_CVC dvca_cert1_reread(Test::data_file("ecc/cvc_chain_cvca.cer")); - result.confirm("signature valid", dvca_ado2.check_signature(*cert_pk)); - result.confirm("signature valid", dvca_ado2.check_signature(dvca_priv_key)); // must also work - - EAC1_1_Req dvca_req2b = dvca_ado2.get_request(); - helper_write_file(dvca_req2b, Test::data_file("ecc/cvc_chain_dvca_req2b.cer")); - result.confirm("files match", helper_files_equal(Test::data_file("ecc/cvc_chain_dvca_req2b.cer"), Test::data_file("ecc/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, Test::rng()); - result.test_eq("DVCA car", dvca_cert2.get_car().iso_8859(), "DECVCA00001"); - result.test_eq("DVCA chr", dvca_cert2.get_chr().iso_8859(), "DEDVCAEPASS00002"); - - // make a first round IS request - ECDSA_PrivateKey is_priv_key(Test::rng(), dom_pars); - EAC1_1_Req is_req = DE_EAC::create_cvc_req(is_priv_key, ASN1_Chr("DEIS"), hash, Test::rng()); - helper_write_file(is_req, Test::data_file("ecc/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, Test::rng()); - result.test_eq("EAC 1.1 CVC car", is_cert1.get_car().iso_8859(), "DEDVCAEPASS00001"); - result.test_eq("EAC 1.1 CVC chr", is_cert1.get_chr().iso_8859(), "DEIS00001"); - helper_write_file(is_cert1, Test::data_file("ecc/cvc_chain_is_cert.cer")); - - // verify the signature of the certificate - result.confirm("valid signature", is_cert1.check_signature(dvca_priv_key)); - - return result; - } - -class CVC_Unit_Tests : public Test - { - public: - std::vector run() override - { - std::vector results; - - std::vector> fns = { - test_cvc_times, - test_enc_gen_selfsigned, - test_enc_gen_req, - test_cvc_req_ext, - test_cvc_ado_creation, - test_cvc_ado_comparison, - test_eac_time, - test_ver_cvca, - test_copy_and_assignment, - test_eac_str_illegal_values, - test_tmp_eac_str_enc, - test_cvc_chain - }; - - for(size_t i = 0; i != fns.size(); ++i) - { - try - { - results.push_back(fns[i]()); - } - catch(std::exception& e) - { - results.push_back(Test::Result::Failure("CVC test " + std::to_string(i), e.what())); - } - } - - return results; - } - - }; - -BOTAN_REGISTER_TEST("cvc", CVC_Unit_Tests); - -#endif - -} - -} -- cgit v1.2.3 From 93899a96dc79199bf31d40f452d40b0c6fba20b7 Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Sun, 24 Apr 2016 06:09:33 -0400 Subject: Remove DN field requirements on generating certs and PKCS #10 I have no idea why this is requiring the country code be set, but for many applications a country is not even meaningful. This change also allows CN to be empty/unset on the request or cert, since there is no actual requirement for any specific DN entry type and RFC 5280 specifically allows even an completely empty DN, with name information only in the subjectAltName extension. This change also allows generating a self-signed cert or cert request that expires before it starts. That could only happen with an explicit decision by the application to set it that way, and there is no harm in returning these non-secret bits. They will probably notice their problem as soon as the cert is rejected by any receiving system. --- src/lib/cert/x509/x509opt.cpp | 13 ------------- src/lib/cert/x509/x509self.cpp | 4 ---- src/lib/cert/x509/x509self.h | 5 ----- 3 files changed, 22 deletions(-) (limited to 'src/lib/cert') diff --git a/src/lib/cert/x509/x509opt.cpp b/src/lib/cert/x509/x509opt.cpp index 158f4c779..2dd2098fe 100644 --- a/src/lib/cert/x509/x509opt.cpp +++ b/src/lib/cert/x509/x509opt.cpp @@ -61,19 +61,6 @@ void X509_Cert_Options::CA_key(size_t limit) path_limit = limit; } -/* -* Do basic sanity checks -*/ -void X509_Cert_Options::sanity_check() const - { - if(common_name.empty() || country.empty()) - throw Encoding_Error("X.509 certificate: name and country MUST be set"); - if(country.size() != 2) - throw Encoding_Error("Invalid ISO country code: " + country); - if(start >= end) - throw Encoding_Error("X509_Cert_Options: invalid time constraints"); - } - /* * Initialize the certificate options */ diff --git a/src/lib/cert/x509/x509self.cpp b/src/lib/cert/x509/x509self.cpp index 7d1c01c37..8b9aeda09 100644 --- a/src/lib/cert/x509/x509self.cpp +++ b/src/lib/cert/x509/x509self.cpp @@ -49,8 +49,6 @@ X509_Certificate create_self_signed_cert(const X509_Cert_Options& opts, X509_DN subject_dn; AlternativeName subject_alt; - opts.sanity_check(); - std::vector pub_key = X509::BER_encode(key); std::unique_ptr signer(choose_sig_format(key, hash_fn, sig_algo)); load_info(opts, subject_dn, subject_alt); @@ -95,8 +93,6 @@ PKCS10_Request create_cert_req(const X509_Cert_Options& opts, X509_DN subject_dn; AlternativeName subject_alt; - opts.sanity_check(); - std::vector pub_key = X509::BER_encode(key); std::unique_ptr signer(choose_sig_format(key, hash_fn, sig_algo)); load_info(opts, subject_dn, subject_alt); diff --git a/src/lib/cert/x509/x509self.h b/src/lib/cert/x509/x509self.h index a4bbad214..401b2eb2f 100644 --- a/src/lib/cert/x509/x509self.h +++ b/src/lib/cert/x509/x509self.h @@ -114,11 +114,6 @@ class BOTAN_DLL X509_Cert_Options */ std::vector ex_constraints; - /** - * Check the options set in this object for validity. - */ - void sanity_check() const; - /** * Mark the certificate as a CA certificate and set the path limit. * @param limit the path limit to be set in the BasicConstraints extension. -- cgit v1.2.3 From 67a215deb593c6850992537cb6cb3f55ab65a6ab Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Sun, 5 Jun 2016 12:10:14 -0400 Subject: Add Not_Implemented exception --- src/lib/cert/x509/name_constraint.cpp | 4 ++-- src/lib/cert/x509/ocsp_types.cpp | 2 +- src/lib/cert/x509/x509_ext.cpp | 8 ++++---- src/lib/utils/exceptn.h | 10 ++++++++++ 4 files changed, 17 insertions(+), 7 deletions(-) (limited to 'src/lib/cert') diff --git a/src/lib/cert/x509/name_constraint.cpp b/src/lib/cert/x509/name_constraint.cpp index a1ed19856..83f6386ba 100644 --- a/src/lib/cert/x509/name_constraint.cpp +++ b/src/lib/cert/x509/name_constraint.cpp @@ -33,7 +33,7 @@ GeneralName::GeneralName(const std::string& v) : GeneralName() void GeneralName::encode_into(class DER_Encoder&) const { - throw Exception("General Name encoding not implemented"); + throw Not_Implemented("GeneralName encoding"); } void GeneralName::decode_from(class BER_Decoder& ber) @@ -249,7 +249,7 @@ GeneralSubtree::GeneralSubtree(const std::string& v) : GeneralSubtree() void GeneralSubtree::encode_into(class DER_Encoder&) const { - throw std::runtime_error("General Subtree encoding not implemented"); + throw Not_Implemented("General Subtree encoding"); } void GeneralSubtree::decode_from(class BER_Decoder& ber) diff --git a/src/lib/cert/x509/ocsp_types.cpp b/src/lib/cert/x509/ocsp_types.cpp index ba5b825f7..d470c2fa1 100644 --- a/src/lib/cert/x509/ocsp_types.cpp +++ b/src/lib/cert/x509/ocsp_types.cpp @@ -92,7 +92,7 @@ void CertID::decode_from(class BER_Decoder& from) void SingleResponse::encode_into(class DER_Encoder&) const { - throw Exception("Not implemented (SingleResponse::encode_into)"); + throw Not_Implemented("SingleResponse::encode_into"); } void SingleResponse::decode_from(class BER_Decoder& from) diff --git a/src/lib/cert/x509/x509_ext.cpp b/src/lib/cert/x509/x509_ext.cpp index 85d40bf21..986254bc9 100644 --- a/src/lib/cert/x509/x509_ext.cpp +++ b/src/lib/cert/x509/x509_ext.cpp @@ -461,7 +461,7 @@ void Extended_Key_Usage::contents_to(Data_Store& subject, Data_Store&) const */ std::vector Name_Constraints::encode_inner() const { - throw std::runtime_error("Name_Constraints encoding not implemented"); + throw Not_Implemented("Name_Constraints encoding"); } @@ -777,7 +777,7 @@ void CRL_ReasonCode::contents_to(Data_Store& info, Data_Store&) const std::vector CRL_Distribution_Points::encode_inner() const { - throw Exception("CRL_Distribution_Points encoding not implemented"); + throw Not_Implemented("CRL_Distribution_Points encoding"); } void CRL_Distribution_Points::decode_inner(const std::vector& buf) @@ -800,7 +800,7 @@ void CRL_Distribution_Points::contents_to(Data_Store& info, Data_Store&) const void CRL_Distribution_Points::Distribution_Point::encode_into(class DER_Encoder&) const { - throw Exception("CRL_Distribution_Points encoding not implemented"); + throw Not_Implemented("CRL_Distribution_Points encoding"); } void CRL_Distribution_Points::Distribution_Point::decode_from(class BER_Decoder& ber) @@ -815,7 +815,7 @@ void CRL_Distribution_Points::Distribution_Point::decode_from(class BER_Decoder& std::vector Unknown_Critical_Extension::encode_inner() const { - throw Exception("Unknown_Critical_Extension encoding not implemented"); + throw Not_Implemented("Unknown_Critical_Extension encoding"); } void Unknown_Critical_Extension::decode_inner(const std::vector& buf) diff --git a/src/lib/utils/exceptn.h b/src/lib/utils/exceptn.h index b6797f0f6..193d78ce9 100644 --- a/src/lib/utils/exceptn.h +++ b/src/lib/utils/exceptn.h @@ -215,6 +215,16 @@ struct BOTAN_DLL Self_Test_Failure : public Internal_Error {} }; +/** +* Not Implemented Exception +*/ +struct BOTAN_DLL Not_Implemented : public Exception + { + explicit Not_Implemented(const std::string& err) : + Exception("Not implemented", err) + {} + }; + } #endif -- cgit v1.2.3 From 2ea6f9b1963795dad74489b41bc7d37f897d7a21 Mon Sep 17 00:00:00 2001 From: Daniel Neus Date: Fri, 17 Jun 2016 11:37:18 +0200 Subject: add PKCS#11 support --- src/lib/cert/x509/x509cert.h | 2 +- src/lib/prov/pkcs11/info.txt | 43 + src/lib/prov/pkcs11/p11.cpp | 769 +++++++ src/lib/prov/pkcs11/p11.h | 2861 +++++++++++++++++++++++++++ src/lib/prov/pkcs11/p11_ecc_key.cpp | 137 ++ src/lib/prov/pkcs11/p11_ecc_key.h | 229 +++ src/lib/prov/pkcs11/p11_ecdh.cpp | 151 ++ src/lib/prov/pkcs11/p11_ecdh.h | 122 ++ src/lib/prov/pkcs11/p11_ecdsa.cpp | 239 +++ src/lib/prov/pkcs11/p11_ecdsa.h | 127 ++ src/lib/prov/pkcs11/p11_mechanism.cpp | 250 +++ src/lib/prov/pkcs11/p11_mechanism.h | 108 + src/lib/prov/pkcs11/p11_module.cpp | 41 + src/lib/prov/pkcs11/p11_module.h | 79 + src/lib/prov/pkcs11/p11_object.cpp | 217 ++ src/lib/prov/pkcs11/p11_object.h | 743 +++++++ src/lib/prov/pkcs11/p11_randomgenerator.cpp | 31 + src/lib/prov/pkcs11/p11_randomgenerator.h | 70 + src/lib/prov/pkcs11/p11_rsa.cpp | 382 ++++ src/lib/prov/pkcs11/p11_rsa.h | 213 ++ src/lib/prov/pkcs11/p11_session.cpp | 89 + src/lib/prov/pkcs11/p11_session.h | 105 + src/lib/prov/pkcs11/p11_slot.cpp | 60 + src/lib/prov/pkcs11/p11_slot.h | 79 + src/lib/prov/pkcs11/p11_x509.cpp | 37 + src/lib/prov/pkcs11/p11_x509.h | 115 ++ src/lib/utils/dyn_load/dyn_load.cpp | 2 +- src/lib/utils/dyn_load/dyn_load.h | 3 +- src/lib/utils/dyn_load/info.txt | 12 +- src/tests/main.cpp | 28 +- src/tests/test_pkcs11.cpp | 42 + src/tests/test_pkcs11.h | 50 + src/tests/test_pkcs11_high_level.cpp | 1509 ++++++++++++++ src/tests/test_pkcs11_low_level.cpp | 852 ++++++++ src/tests/tests.cpp | 37 +- src/tests/tests.h | 39 +- 36 files changed, 9829 insertions(+), 44 deletions(-) create mode 100644 src/lib/prov/pkcs11/info.txt create mode 100644 src/lib/prov/pkcs11/p11.cpp create mode 100644 src/lib/prov/pkcs11/p11.h create mode 100644 src/lib/prov/pkcs11/p11_ecc_key.cpp create mode 100644 src/lib/prov/pkcs11/p11_ecc_key.h create mode 100644 src/lib/prov/pkcs11/p11_ecdh.cpp create mode 100644 src/lib/prov/pkcs11/p11_ecdh.h create mode 100644 src/lib/prov/pkcs11/p11_ecdsa.cpp create mode 100644 src/lib/prov/pkcs11/p11_ecdsa.h create mode 100644 src/lib/prov/pkcs11/p11_mechanism.cpp create mode 100644 src/lib/prov/pkcs11/p11_mechanism.h create mode 100644 src/lib/prov/pkcs11/p11_module.cpp create mode 100644 src/lib/prov/pkcs11/p11_module.h create mode 100644 src/lib/prov/pkcs11/p11_object.cpp create mode 100644 src/lib/prov/pkcs11/p11_object.h create mode 100644 src/lib/prov/pkcs11/p11_randomgenerator.cpp create mode 100644 src/lib/prov/pkcs11/p11_randomgenerator.h create mode 100644 src/lib/prov/pkcs11/p11_rsa.cpp create mode 100644 src/lib/prov/pkcs11/p11_rsa.h create mode 100644 src/lib/prov/pkcs11/p11_session.cpp create mode 100644 src/lib/prov/pkcs11/p11_session.h create mode 100644 src/lib/prov/pkcs11/p11_slot.cpp create mode 100644 src/lib/prov/pkcs11/p11_slot.h create mode 100644 src/lib/prov/pkcs11/p11_x509.cpp create mode 100644 src/lib/prov/pkcs11/p11_x509.h create mode 100644 src/tests/test_pkcs11.cpp create mode 100644 src/tests/test_pkcs11.h create mode 100644 src/tests/test_pkcs11_high_level.cpp create mode 100644 src/tests/test_pkcs11_low_level.cpp (limited to 'src/lib/cert') diff --git a/src/lib/cert/x509/x509cert.h b/src/lib/cert/x509/x509cert.h index c521cf7ca..2875c8159 100644 --- a/src/lib/cert/x509/x509cert.h +++ b/src/lib/cert/x509/x509cert.h @@ -33,7 +33,7 @@ enum class Usage_Type /** * This class represents X.509 Certificate */ -class BOTAN_DLL X509_Certificate final : public X509_Object +class BOTAN_DLL X509_Certificate : public X509_Object { public: /** diff --git a/src/lib/prov/pkcs11/info.txt b/src/lib/prov/pkcs11/info.txt new file mode 100644 index 000000000..4d54446da --- /dev/null +++ b/src/lib/prov/pkcs11/info.txt @@ -0,0 +1,43 @@ +define PKCS11 20160219 + +load_on vendor + + +dyn_load +rng +pk_pad +rfc6979 + + + +p11_mechanism.h + + + +p11.h +p11_ecc_key.h +p11_ecdh.h +p11_ecdsa.h +p11_module.h +p11_object.h +p11_randomgenerator.h +p11_rsa.h +p11_session.h +p11_slot.h +p11_x509.h + + + +p11.cpp +p11_ecc_key.cpp +p11_ecdh.cpp +p11_ecdsa.cpp +p11_mechanism.cpp +p11_module.cpp +p11_object.cpp +p11_randomgenerator.cpp +p11_rsa.cpp +p11_session.cpp +p11_slot.cpp +p11_x509.cpp + \ No newline at end of file diff --git a/src/lib/prov/pkcs11/p11.cpp b/src/lib/prov/pkcs11/p11.cpp new file mode 100644 index 000000000..d338438d3 --- /dev/null +++ b/src/lib/prov/pkcs11/p11.cpp @@ -0,0 +1,769 @@ +/* +* PKCS#11 +* (C) 2016 Daniel Neus, Sirrix AG +* (C) 2016 Philipp Weber, Sirrix AG +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include +#include + +#include +#include +#include + +namespace Botan { +namespace PKCS11 { + +ReturnValue* ThrowException = reinterpret_cast< ReturnValue* >(-1); + +namespace { +/// @param function_result Return value of the PKCS11 module function +/// @param returnValue if (`ThrowException`) is passed the function throws an exception, otherwise if a non-NULL pointer is passed: +/// return_value receives the return value of the PKCS#11 function and no exception is thrown. +/// @return true if function call was successful, false otherwise +bool handle_return_value(const CK_RV function_result, ReturnValue* return_value) + { + if(return_value == ThrowException) + { + if(static_cast< ReturnValue >(function_result) != ReturnValue::OK) + { + // caller wants exception + throw PKCS11_ReturnError(static_cast< ReturnValue >(function_result)); + } + } + else if(return_value != nullptr) + { + // caller wants return value + *return_value = static_cast< ReturnValue >(function_result); + } + + return static_cast< ReturnValue >(function_result) == ReturnValue::OK; + } +} + +void initialize_token(Slot& slot, const std::string& label, const secure_string& so_pin, const secure_string& pin) + { + slot.initialize(label, so_pin); + set_pin(slot, so_pin, pin); + } + +void change_pin(Slot& slot, const secure_string& old_pin, const secure_string& new_pin) + { + Session session(slot, false); + session.login(UserType::User, old_pin); + session.set_pin(old_pin, new_pin); + } + +void change_so_pin(Slot& slot, const secure_string& old_so_pin, const secure_string& new_so_pin) + { + Session session(slot, false); + session.login(UserType::SO, old_so_pin); + session.set_pin(old_so_pin, new_so_pin); + } + +void set_pin(Slot& slot, const secure_string& so_pin, const secure_string& pin) + { + Session session(slot, false); + session.login(UserType::SO, so_pin); + session.init_pin(pin); + } + +LowLevel::LowLevel(FunctionListPtr ptr) : + m_func_list_ptr(ptr) + { + if(m_func_list_ptr == nullptr) + { + throw Invalid_Argument("Invalid PKCS#11 function list ptr"); + } + } + +LowLevel::~LowLevel() BOTAN_NOEXCEPT +{} + +/****************************** General purpose functions ******************************/ + +bool LowLevel::C_Initialize(VoidPtr init_args, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_Initialize(init_args), return_value); + } + +bool LowLevel::C_Finalize(VoidPtr reserved, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_Finalize(reserved), return_value); + } + +bool LowLevel::C_GetInfo(Info* info_ptr, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_GetInfo(info_ptr), return_value); + } + +bool LowLevel::C_GetFunctionList(Dynamically_Loaded_Library& pkcs11_module, FunctionListPtr* function_list_ptr_ptr, + ReturnValue* return_value) + { + using get_function_list = CK_RV(*)(FunctionListPtr*); + + get_function_list get_function_list_ptr = pkcs11_module.resolve("C_GetFunctionList"); + + return handle_return_value(get_function_list_ptr(function_list_ptr_ptr), return_value); + } + +/****************************** Slot and token management functions ******************************/ + +bool LowLevel::C_GetSlotList(Bbool token_present, + SlotId* slot_list_ptr, + Ulong* count_ptr, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_GetSlotList(token_present, slot_list_ptr, count_ptr), return_value); + } + +bool LowLevel::C_GetSlotList(bool token_present, + std::vector& slot_ids, + ReturnValue* return_value) const + { + slot_ids.clear(); + + // first get available slots + Ulong number_slots = 0; + + bool success = C_GetSlotList(token_present, nullptr, &number_slots, return_value); + + if(!success || !number_slots) + { + return success; + } + + // get actual slot ids + slot_ids.resize(number_slots); + return C_GetSlotList(token_present, slot_ids.data(), &number_slots, return_value); + } + +bool LowLevel::C_GetSlotInfo(SlotId slot_id, + SlotInfo* info_ptr, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_GetSlotInfo(slot_id, info_ptr), return_value); + } + +bool LowLevel::C_GetTokenInfo(SlotId slot_id, + TokenInfo* info_ptr, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_GetTokenInfo(slot_id, info_ptr), return_value); + } + +bool LowLevel::C_WaitForSlotEvent(Flags flags, + SlotId* slot_ptr, + VoidPtr reserved, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_WaitForSlotEvent(flags, slot_ptr, reserved), return_value); + } + +bool LowLevel::C_GetMechanismList(SlotId slot_id, + MechanismType* mechanism_list_ptr, + Ulong* count_ptr, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_GetMechanismList(slot_id, + reinterpret_cast< CK_MECHANISM_TYPE_PTR >(mechanism_list_ptr), count_ptr), return_value); + } + +bool LowLevel::C_GetMechanismList(SlotId slot_id, + std::vector& mechanisms, + ReturnValue* return_value) const + { + mechanisms.clear(); + + // first get number of mechanisms + Ulong number_mechanisms = 0; + + bool success = C_GetMechanismList(slot_id, nullptr, &number_mechanisms, return_value); + + if(!success || !number_mechanisms) + { + return success; + } + + // get actual mechanisms + mechanisms.resize(number_mechanisms); + return C_GetMechanismList(slot_id, reinterpret_cast< MechanismType* >(mechanisms.data()), &number_mechanisms, + return_value); + } + +bool LowLevel::C_GetMechanismInfo(SlotId slot_id, + MechanismType type, + MechanismInfo* info_ptr, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_GetMechanismInfo(slot_id, static_cast< CK_MECHANISM_TYPE >(type), + info_ptr), return_value); + } + +bool LowLevel::C_InitToken(SlotId slot_id, + Utf8Char* so_pin_ptr, + Ulong so_pin_len, + Utf8Char* label_ptr, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_InitToken(slot_id, so_pin_ptr, so_pin_len, label_ptr), return_value); + } + +bool LowLevel::C_InitPIN(SessionHandle session, + Utf8Char* pin_ptr, + Ulong pin_len, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_InitPIN(session, pin_ptr, pin_len), return_value); + } + +bool LowLevel::C_SetPIN(SessionHandle session, + Utf8Char* old_pin_ptr, + Ulong old_len, + Utf8Char* new_pin_ptr, + Ulong new_len, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_SetPIN(session, old_pin_ptr, old_len, new_pin_ptr, new_len), + return_value); + } + +/****************************** Session management ******************************/ + +bool LowLevel::C_OpenSession(SlotId slot_id, + Flags flags, + VoidPtr application, + Notify notify, + SessionHandle* session_ptr, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_OpenSession(slot_id, flags, application, notify, session_ptr), + return_value); + } + +bool LowLevel::C_CloseSession(SessionHandle session, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_CloseSession(session), return_value); + } + +bool LowLevel::C_CloseAllSessions(SlotId slot_id, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_CloseAllSessions(slot_id), return_value); + } + +bool LowLevel::C_GetSessionInfo(SessionHandle session, + SessionInfo* info_ptr, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_GetSessionInfo(session, info_ptr), return_value); + } + +bool LowLevel::C_GetOperationState(SessionHandle session, + Byte* operation_state_ptr, + Ulong* operation_state_len_ptr, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_GetOperationState(session, operation_state_ptr, operation_state_len_ptr), + return_value); + } + +bool LowLevel::C_SetOperationState(SessionHandle session, + Byte* operation_state_ptr, + Ulong operation_state_len, + ObjectHandle encryption_key, + ObjectHandle authentication_key, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_SetOperationState(session, operation_state_ptr, operation_state_len, + encryption_key, authentication_key), return_value); + } + +bool LowLevel::C_Login(SessionHandle session, + UserType user_type, + Utf8Char* pin_ptr, + Ulong pin_len, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_Login(session, static_cast< CK_USER_TYPE >(user_type), pin_ptr, pin_len), + return_value); + } + +bool LowLevel::C_Logout(SessionHandle session, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_Logout(session), return_value); + } + +/****************************** Object management functions ******************************/ + +bool LowLevel::C_CreateObject(SessionHandle session, + Attribute* attribute_template_ptr, + Ulong count, + ObjectHandle* object_ptr, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_CreateObject(session, attribute_template_ptr, count, object_ptr), + return_value); + } + +bool LowLevel::C_CopyObject(SessionHandle session, + ObjectHandle object, + Attribute* attribute_template_ptr, + Ulong count, + ObjectHandle* new_object_ptr, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_CopyObject(session, object, attribute_template_ptr, count, + new_object_ptr), return_value); + } + +bool LowLevel::C_DestroyObject(SessionHandle session, + ObjectHandle object, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_DestroyObject(session, object), return_value); + } + +bool LowLevel::C_GetObjectSize(SessionHandle session, + ObjectHandle object, + Ulong* size_ptr, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_GetObjectSize(session, object, size_ptr), return_value); + } + +bool LowLevel::C_GetAttributeValue(SessionHandle session, + ObjectHandle object, + Attribute* attribute_template_ptr, + Ulong count, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_GetAttributeValue(session, object, attribute_template_ptr, count), + return_value); + } + +bool LowLevel::C_SetAttributeValue(SessionHandle session, + ObjectHandle object, + Attribute* attribute_template_ptr, + Ulong count, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_SetAttributeValue(session, object, attribute_template_ptr, count), + return_value); + } + +bool LowLevel::C_FindObjectsInit(SessionHandle session, + Attribute* attribute_template_ptr, + Ulong count, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_FindObjectsInit(session, attribute_template_ptr, count), return_value); + } + +bool LowLevel::C_FindObjects(SessionHandle session, + ObjectHandle* object_ptr, + Ulong max_object_count, + Ulong* object_count_ptr, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_FindObjects(session, object_ptr, max_object_count, object_count_ptr), + return_value); + } + +bool LowLevel::C_FindObjectsFinal(SessionHandle session, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_FindObjectsFinal(session), return_value); + } + +/****************************** Encryption functions ******************************/ + +bool LowLevel::C_EncryptInit(SessionHandle session, + Mechanism* mechanism_ptr, + ObjectHandle key, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_EncryptInit(session, mechanism_ptr, key), return_value); + } + +bool LowLevel::C_Encrypt(SessionHandle session, + Byte* data_ptr, + Ulong data_len, + Byte* encrypted_data_ptr, + Ulong* encrypted_data_len_ptr, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_Encrypt(session, data_ptr, data_len, encrypted_data_ptr, + encrypted_data_len_ptr), return_value); + } + +bool LowLevel::C_EncryptUpdate(SessionHandle session, + Byte* part_ptr, + Ulong part_len, + Byte* encrypted_part_ptr, + Ulong* encrypted_part_len_ptr, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_EncryptUpdate(session, part_ptr, part_len, encrypted_part_ptr, + encrypted_part_len_ptr), return_value); + } + +bool LowLevel::C_EncryptFinal(SessionHandle session, + Byte* last_encrypted_part_ptr, + Ulong* last_encrypted_part_len_ptr, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_EncryptFinal(session, last_encrypted_part_ptr, + last_encrypted_part_len_ptr), return_value); + } + +/****************************** Decryption functions ******************************/ + +bool LowLevel::C_DecryptInit(SessionHandle session, + Mechanism* mechanism_ptr, + ObjectHandle key, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_DecryptInit(session, mechanism_ptr, key), return_value); + } + +bool LowLevel::C_Decrypt(SessionHandle session, + Byte* encrypted_data_ptr, + Ulong encrypted_data_len, + Byte* data_ptr, + Ulong* data_len_ptr, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_Decrypt(session, encrypted_data_ptr, encrypted_data_len, data_ptr, + data_len_ptr), return_value); + } + +bool LowLevel::C_DecryptUpdate(SessionHandle session, + Byte* encrypted_part_ptr, + Ulong encrypted_part_len, + Byte* part_ptr, + Ulong* part_len_ptr, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_DecryptUpdate(session, encrypted_part_ptr, encrypted_part_len, part_ptr, + part_len_ptr), return_value); + } + +bool LowLevel::C_DecryptFinal(SessionHandle session, + Byte* last_part_ptr, + Ulong* last_part_len_ptr, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_DecryptFinal(session, last_part_ptr, last_part_len_ptr), return_value); + } + +/****************************** Message digesting functions ******************************/ + +bool LowLevel::C_DigestInit(SessionHandle session, + Mechanism* mechanism, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_DigestInit(session, mechanism), return_value); + } + +bool LowLevel::C_Digest(SessionHandle session, + Byte* data_ptr, + Ulong data_len, + Byte* digest_ptr, + Ulong* digest_len_ptr, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_Digest(session, data_ptr, data_len, digest_ptr, digest_len_ptr), + return_value); + } + +bool LowLevel::C_DigestUpdate(SessionHandle session, + Byte* part_ptr, + Ulong part_len, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_DigestUpdate(session, part_ptr, part_len), return_value); + } + +bool LowLevel::C_DigestKey(SessionHandle session, + ObjectHandle key, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_DigestKey(session, key), return_value); + } + +bool LowLevel::C_DigestFinal(SessionHandle session, + Byte* digest_ptr, + Ulong* digest_len_ptr, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_DigestFinal(session, digest_ptr, digest_len_ptr), return_value); + } + +/****************************** Signing and MACing functions ******************************/ + +bool LowLevel::C_SignInit(SessionHandle session, + Mechanism* mechanism_ptr, + ObjectHandle key, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_SignInit(session, mechanism_ptr, key), return_value); + } + +bool LowLevel::C_Sign(SessionHandle session, + Byte* data_ptr, + Ulong data_len, + Byte* signature_ptr, + Ulong* signature_len_ptr, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_Sign(session, data_ptr, data_len, signature_ptr, signature_len_ptr), + return_value); + } + +bool LowLevel::C_SignUpdate(SessionHandle session, + Byte* part_ptr, + Ulong part_len, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_SignUpdate(session, part_ptr, part_len), return_value); + } + +bool LowLevel::C_SignFinal(SessionHandle session, + Byte* signature_ptr, + Ulong* signature_len_ptr, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_SignFinal(session, signature_ptr, signature_len_ptr), return_value); + } + +bool LowLevel::C_SignRecoverInit(SessionHandle session, + Mechanism* mechanism_ptr, + ObjectHandle key, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_SignRecoverInit(session, mechanism_ptr, key), return_value); + } + +bool LowLevel::C_SignRecover(SessionHandle session, + Byte* data, + Ulong data_len, + Byte* signature, + Ulong* signature_len, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_SignRecover(session, data, data_len, signature, signature_len), + return_value); + } + +/****************************** Functions for verifying signatures and MACs ******************************/ + +bool LowLevel::C_VerifyInit(SessionHandle session, + Mechanism* mechanism_ptr, + ObjectHandle key, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_VerifyInit(session, mechanism_ptr, key), return_value); + } + +bool LowLevel::C_Verify(SessionHandle session, + Byte* data_ptr, + Ulong data_len, + Byte* signature_ptr, + Ulong signature_len, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_Verify(session, data_ptr, data_len, signature_ptr, signature_len), + return_value); + } + +bool LowLevel::C_VerifyUpdate(SessionHandle session, + Byte* part_ptr, + Ulong part_len, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_VerifyUpdate(session, part_ptr, part_len), return_value); + } + +bool LowLevel::C_VerifyFinal(SessionHandle session, + Byte* signature_ptr, + Ulong signature_len, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_VerifyFinal(session, signature_ptr, signature_len), return_value); + } + +bool LowLevel::C_VerifyRecoverInit(SessionHandle session, + Mechanism* mechanism_ptr, + ObjectHandle key, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_VerifyRecoverInit(session, mechanism_ptr, key), return_value); + } + +bool LowLevel::C_VerifyRecover(SessionHandle session, + Byte* signature_ptr, + Ulong signature_len, + Byte* data_ptr, + Ulong* data_len_ptr, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_VerifyRecover(session, signature_ptr, signature_len, data_ptr, + data_len_ptr), return_value); + } + +/****************************** Dual-purpose cryptographic functions ******************************/ + +bool LowLevel::C_DigestEncryptUpdate(SessionHandle session, + Byte* part_ptr, + Ulong part_len, + Byte* encrypted_part_ptr, + Ulong* encrypted_part_len_ptr, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_DigestEncryptUpdate(session, part_ptr, part_len, encrypted_part_ptr, + encrypted_part_len_ptr), return_value); + } + +bool LowLevel::C_DecryptDigestUpdate(SessionHandle session, + Byte* encrypted_part_ptr, + Ulong encrypted_part_len, + Byte* part_ptr, + Ulong* part_len_ptr, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_DecryptDigestUpdate(session, encrypted_part_ptr, encrypted_part_len, + part_ptr, part_len_ptr), return_value); + } + +bool LowLevel::C_SignEncryptUpdate(SessionHandle session, + Byte* part_ptr, + Ulong part_len, + Byte* encrypted_part_ptr, + Ulong* encrypted_part_len_ptr, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_SignEncryptUpdate(session, part_ptr, part_len, encrypted_part_ptr, + encrypted_part_len_ptr), return_value); + } + +bool LowLevel::C_DecryptVerifyUpdate(SessionHandle session, + Byte* encrypted_part_ptr, + Ulong encrypted_part_len, + Byte* part_ptr, + Ulong* part_len_ptr, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_DecryptVerifyUpdate(session, encrypted_part_ptr, encrypted_part_len, + part_ptr, part_len_ptr), return_value); + } + +/****************************** Key management functions ******************************/ + +bool LowLevel::C_GenerateKey(SessionHandle session, + Mechanism* mechanism_ptr, + Attribute* attribute_template_ptr, + Ulong count, + ObjectHandle* key_ptr, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_GenerateKey(session, mechanism_ptr, attribute_template_ptr, count, + key_ptr), return_value); + } + +bool LowLevel::C_GenerateKeyPair(SessionHandle session, + Mechanism* mechanism_ptr, + Attribute* public_key_template_ptr, + Ulong public_key_attribute_count, + Attribute* private_key_template_ptr, + Ulong private_key_attribute_count, + ObjectHandle* public_key_ptr, + ObjectHandle* private_key_ptr, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_GenerateKeyPair(session, mechanism_ptr, public_key_template_ptr, + public_key_attribute_count, private_key_template_ptr, + private_key_attribute_count, public_key_ptr, private_key_ptr), return_value); + } + +bool LowLevel::C_WrapKey(SessionHandle session, + Mechanism* mechanism_ptr, + ObjectHandle wrapping_key, + ObjectHandle key, + Byte* wrapped_key_ptr, + Ulong* wrapped_key_len_ptr, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_WrapKey(session, mechanism_ptr, wrapping_key, key, wrapped_key_ptr, + wrapped_key_len_ptr), return_value); + } + +bool LowLevel::C_UnwrapKey(SessionHandle session, + Mechanism* mechanism_ptr, + ObjectHandle unwrapping_key, + Byte* wrapped_key_ptr, + Ulong wrapped_key_len, + Attribute* attribute_template_ptr, + Ulong attribute_count, + ObjectHandle* key_ptr, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_UnwrapKey(session, mechanism_ptr, unwrapping_key, wrapped_key_ptr, + wrapped_key_len, attribute_template_ptr, + attribute_count, key_ptr), return_value); + } + +bool LowLevel::C_DeriveKey(SessionHandle session, + Mechanism* mechanism_ptr, + ObjectHandle base_key, + Attribute* attribute_template_ptr, + Ulong attribute_count, + ObjectHandle* key_ptr, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_DeriveKey(session, mechanism_ptr, base_key, attribute_template_ptr, + attribute_count, key_ptr), return_value); + } + +/****************************** Random number generation functions ******************************/ + +bool LowLevel::C_SeedRandom(SessionHandle session, + Byte* seed_ptr, + Ulong seed_len, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_SeedRandom(session, seed_ptr, seed_len), return_value); + } + +bool LowLevel::C_GenerateRandom(SessionHandle session, + Byte* random_data_ptr, + Ulong random_len, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_GenerateRandom(session, random_data_ptr, random_len), return_value); + } + +/****************************** Parallel function management functions ******************************/ + +bool LowLevel::C_GetFunctionStatus(SessionHandle session, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_GetFunctionStatus(session), return_value); + } + +bool LowLevel::C_CancelFunction(SessionHandle session, + ReturnValue* return_value) const + { + return handle_return_value(m_func_list_ptr->C_CancelFunction(session), return_value); + } + +} + +} diff --git a/src/lib/prov/pkcs11/p11.h b/src/lib/prov/pkcs11/p11.h new file mode 100644 index 000000000..c18c07d59 --- /dev/null +++ b/src/lib/prov/pkcs11/p11.h @@ -0,0 +1,2861 @@ +/* +* PKCS#11 +* (C) 2016 Daniel Neus, Sirrix AG +* (C) 2016 Philipp Weber, Sirrix AG +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_P11_H__ +#define BOTAN_P11_H__ + +#include +#include +#include + +#include +#include +#include + +#define BOTAN_PKCS11_RSA_PRIO 90 +#define BOTAN_PKCS11_ECDSA_PRIO 90 +#define BOTAN_PKCS11_ECDH_PRIO 90 + +#define CK_PTR * + +#if defined(_MSC_VER) +#define CK_DECLARE_FUNCTION(returnType, name) \ + returnType __declspec(dllimport) name +#else +#define CK_DECLARE_FUNCTION(returnType, name) \ + returnType name +#endif + +#if defined(_MSC_VER) +#define CK_DECLARE_FUNCTION_POINTER(returnType, name) \ + returnType __declspec(dllimport) (* name) +#else +#define CK_DECLARE_FUNCTION_POINTER(returnType, name) \ + returnType (* name) +#endif + +#define CK_CALLBACK_FUNCTION(returnType, name) \ + returnType (* name) + +#ifndef NULL_PTR + #define NULL_PTR nullptr +#endif + +#if defined(_MSC_VER) + #pragma pack(push, cryptoki, 1) +#endif + +#include "pkcs11.h" + +#if defined(_MSC_VER) + #pragma pack(pop, cryptoki) +#endif + +static_assert(CRYPTOKI_VERSION_MAJOR == 2 && CRYPTOKI_VERSION_MINOR == 40, + "The Botan PKCS#11 module was implemented against PKCS#11 v2.40. Please use the correct PKCS#11 headers."); + +namespace Botan { +namespace PKCS11 { + +using secure_string = secure_vector; + +enum class AttributeType : CK_ATTRIBUTE_TYPE + { + Class = CKA_CLASS, + Token = CKA_TOKEN, + Private = CKA_PRIVATE, + Label = CKA_LABEL, + Application = CKA_APPLICATION, + Value = CKA_VALUE, + ObjectId = CKA_OBJECT_ID, + CertificateType = CKA_CERTIFICATE_TYPE, + Issuer = CKA_ISSUER, + SerialNumber = CKA_SERIAL_NUMBER, + AcIssuer = CKA_AC_ISSUER, + Owner = CKA_OWNER, + AttrTypes = CKA_ATTR_TYPES, + Trusted = CKA_TRUSTED, + CertificateCategory = CKA_CERTIFICATE_CATEGORY, + JavaMidpSecurityDomain = CKA_JAVA_MIDP_SECURITY_DOMAIN, + Url = CKA_URL, + HashOfSubjectPublicKey = CKA_HASH_OF_SUBJECT_PUBLIC_KEY, + HashOfIssuerPublicKey = CKA_HASH_OF_ISSUER_PUBLIC_KEY, + NameHashAlgorithm = CKA_NAME_HASH_ALGORITHM, + CheckValue = CKA_CHECK_VALUE, + KeyType = CKA_KEY_TYPE, + Subject = CKA_SUBJECT, + Id = CKA_ID, + Sensitive = CKA_SENSITIVE, + Encrypt = CKA_ENCRYPT, + Decrypt = CKA_DECRYPT, + Wrap = CKA_WRAP, + Unwrap = CKA_UNWRAP, + Sign = CKA_SIGN, + SignRecover = CKA_SIGN_RECOVER, + Verify = CKA_VERIFY, + VerifyRecover = CKA_VERIFY_RECOVER, + Derive = CKA_DERIVE, + StartDate = CKA_START_DATE, + EndDate = CKA_END_DATE, + Modulus = CKA_MODULUS, + ModulusBits = CKA_MODULUS_BITS, + PublicExponent = CKA_PUBLIC_EXPONENT, + PrivateExponent = CKA_PRIVATE_EXPONENT, + Prime1 = CKA_PRIME_1, + Prime2 = CKA_PRIME_2, + Exponent1 = CKA_EXPONENT_1, + Exponent2 = CKA_EXPONENT_2, + Coefficient = CKA_COEFFICIENT, + PublicKeyInfo = CKA_PUBLIC_KEY_INFO, + Prime = CKA_PRIME, + Subprime = CKA_SUBPRIME, + Base = CKA_BASE, + PrimeBits = CKA_PRIME_BITS, + SubprimeBits = CKA_SUBPRIME_BITS, + SubPrimeBits = CKA_SUB_PRIME_BITS, + ValueBits = CKA_VALUE_BITS, + ValueLen = CKA_VALUE_LEN, + Extractable = CKA_EXTRACTABLE, + Local = CKA_LOCAL, + NeverExtractable = CKA_NEVER_EXTRACTABLE, + AlwaysSensitive = CKA_ALWAYS_SENSITIVE, + KeyGenMechanism = CKA_KEY_GEN_MECHANISM, + Modifiable = CKA_MODIFIABLE, + Copyable = CKA_COPYABLE, + Destroyable = CKA_DESTROYABLE, + EcdsaParams = CKA_ECDSA_PARAMS, + EcParams = CKA_EC_PARAMS, + EcPoint = CKA_EC_POINT, + SecondaryAuth = CKA_SECONDARY_AUTH, + AuthPinFlags = CKA_AUTH_PIN_FLAGS, + AlwaysAuthenticate = CKA_ALWAYS_AUTHENTICATE, + WrapWithTrusted = CKA_WRAP_WITH_TRUSTED, + WrapTemplate = CKA_WRAP_TEMPLATE, + UnwrapTemplate = CKA_UNWRAP_TEMPLATE, + DeriveTemplate = CKA_DERIVE_TEMPLATE, + OtpFormat = CKA_OTP_FORMAT, + OtpLength = CKA_OTP_LENGTH, + OtpTimeInterval = CKA_OTP_TIME_INTERVAL, + OtpUserFriendlyMode = CKA_OTP_USER_FRIENDLY_MODE, + OtpChallengeRequirement = CKA_OTP_CHALLENGE_REQUIREMENT, + OtpTimeRequirement = CKA_OTP_TIME_REQUIREMENT, + OtpCounterRequirement = CKA_OTP_COUNTER_REQUIREMENT, + OtpPinRequirement = CKA_OTP_PIN_REQUIREMENT, + OtpCounter = CKA_OTP_COUNTER, + OtpTime = CKA_OTP_TIME, + OtpUserIdentifier = CKA_OTP_USER_IDENTIFIER, + OtpServiceIdentifier = CKA_OTP_SERVICE_IDENTIFIER, + OtpServiceLogo = CKA_OTP_SERVICE_LOGO, + OtpServiceLogoType = CKA_OTP_SERVICE_LOGO_TYPE, + Gostr3410Params = CKA_GOSTR3410_PARAMS, + Gostr3411Params = CKA_GOSTR3411_PARAMS, + Gost28147Params = CKA_GOST28147_PARAMS, + HwFeatureType = CKA_HW_FEATURE_TYPE, + ResetOnInit = CKA_RESET_ON_INIT, + HasReset = CKA_HAS_RESET, + PixelX = CKA_PIXEL_X, + PixelY = CKA_PIXEL_Y, + Resolution = CKA_RESOLUTION, + CharRows = CKA_CHAR_ROWS, + CharColumns = CKA_CHAR_COLUMNS, + Color = CKA_COLOR, + BitsPerPixel = CKA_BITS_PER_PIXEL, + CharSets = CKA_CHAR_SETS, + EncodingMethods = CKA_ENCODING_METHODS, + MimeTypes = CKA_MIME_TYPES, + MechanismType = CKA_MECHANISM_TYPE, + RequiredCmsAttributes = CKA_REQUIRED_CMS_ATTRIBUTES, + DefaultCmsAttributes = CKA_DEFAULT_CMS_ATTRIBUTES, + SupportedCmsAttributes = CKA_SUPPORTED_CMS_ATTRIBUTES, + AllowedMechanisms = CKA_ALLOWED_MECHANISMS, + VendorDefined = CKA_VENDOR_DEFINED, + }; + +enum class CertificateType : CK_CERTIFICATE_TYPE + { + X509 = CKC_X_509, + X509AttrCert = CKC_X_509_ATTR_CERT, + Wtls = CKC_WTLS, + VendorDefined = CKC_VENDOR_DEFINED, + }; + +/// Indicates if a stored certificate is a user certificate for which the corresponding private key is available +/// on the token ("token user"), a CA certificate ("authority"), or another end-entity certificate ("other entity"). +enum class CertificateCategory : CK_ULONG + { + Unspecified = CK_CERTIFICATE_CATEGORY_UNSPECIFIED, + TokenUser = CK_CERTIFICATE_CATEGORY_TOKEN_USER, + Authority = CK_CERTIFICATE_CATEGORY_AUTHORITY, + OtherEntity = CK_CERTIFICATE_CATEGORY_OTHER_ENTITY + }; + +enum class KeyDerivation : CK_ULONG + { + Null = CKD_NULL, + Sha1Kdf = CKD_SHA1_KDF, + Sha1KdfAsn1 = CKD_SHA1_KDF_ASN1, + Sha1KdfConcatenate = CKD_SHA1_KDF_CONCATENATE, + Sha224Kdf = CKD_SHA224_KDF, + Sha256Kdf = CKD_SHA256_KDF, + Sha384Kdf = CKD_SHA384_KDF, + Sha512Kdf = CKD_SHA512_KDF, + CpdiversifyKdf = CKD_CPDIVERSIFY_KDF, + }; + +enum class Flag : CK_FLAGS + { + None = 0, + TokenPresent = CKF_TOKEN_PRESENT, + RemovableDevice = CKF_REMOVABLE_DEVICE, + HwSlot = CKF_HW_SLOT, + Rng = CKF_RNG, + WriteProtected = CKF_WRITE_PROTECTED, + LoginRequired = CKF_LOGIN_REQUIRED, + UserPinInitialized = CKF_USER_PIN_INITIALIZED, + RestoreKeyNotNeeded = CKF_RESTORE_KEY_NOT_NEEDED, + ClockOnToken = CKF_CLOCK_ON_TOKEN, + ProtectedAuthenticationPath = CKF_PROTECTED_AUTHENTICATION_PATH, + DualCryptoOperations = CKF_DUAL_CRYPTO_OPERATIONS, + TokenInitialized = CKF_TOKEN_INITIALIZED, + SecondaryAuthentication = CKF_SECONDARY_AUTHENTICATION, + UserPinCountLow = CKF_USER_PIN_COUNT_LOW, + UserPinFinalTry = CKF_USER_PIN_FINAL_TRY, + UserPinLocked = CKF_USER_PIN_LOCKED, + UserPinToBeChanged = CKF_USER_PIN_TO_BE_CHANGED, + SoPinCountLow = CKF_SO_PIN_COUNT_LOW, + SoPinFinalTry = CKF_SO_PIN_FINAL_TRY, + SoPinLocked = CKF_SO_PIN_LOCKED, + SoPinToBeChanged = CKF_SO_PIN_TO_BE_CHANGED, + ErrorState = CKF_ERROR_STATE, + RwSession = CKF_RW_SESSION, + SerialSession = CKF_SERIAL_SESSION, + ArrayAttribute = CKF_ARRAY_ATTRIBUTE, + Hw = CKF_HW, + Encrypt = CKF_ENCRYPT, + Decrypt = CKF_DECRYPT, + Digest = CKF_DIGEST, + Sign = CKF_SIGN, + SignRecover = CKF_SIGN_RECOVER, + Verify = CKF_VERIFY, + VerifyRecover = CKF_VERIFY_RECOVER, + Generate = CKF_GENERATE, + GenerateKeyPair = CKF_GENERATE_KEY_PAIR, + Wrap = CKF_WRAP, + Unwrap = CKF_UNWRAP, + Derive = CKF_DERIVE, + EcFP = CKF_EC_F_P, + EcF2m = CKF_EC_F_2M, + EcEcparameters = CKF_EC_ECPARAMETERS, + EcNamedcurve = CKF_EC_NAMEDCURVE, + EcUncompress = CKF_EC_UNCOMPRESS, + EcCompress = CKF_EC_COMPRESS, + Extension = CKF_EXTENSION, + LibraryCantCreateOsThreads = CKF_LIBRARY_CANT_CREATE_OS_THREADS, + OsLockingOk = CKF_OS_LOCKING_OK, + DontBlock = CKF_DONT_BLOCK, + NextOtp = CKF_NEXT_OTP, + ExcludeTime = CKF_EXCLUDE_TIME, + ExcludeCounter = CKF_EXCLUDE_COUNTER, + ExcludeChallenge = CKF_EXCLUDE_CHALLENGE, + ExcludePin = CKF_EXCLUDE_PIN, + UserFriendlyOtp = CKF_USER_FRIENDLY_OTP, + }; + +inline Flag operator | (Flag a, Flag b) + { + return static_cast< Flag >(static_cast< CK_FLAGS >(a) | static_cast< CK_FLAGS >(b)); + } + +enum class MGF : CK_RSA_PKCS_MGF_TYPE + { + Mgf1Sha1 = CKG_MGF1_SHA1, + Mgf1Sha256 = CKG_MGF1_SHA256, + Mgf1Sha384 = CKG_MGF1_SHA384, + Mgf1Sha512 = CKG_MGF1_SHA512, + Mgf1Sha224 = CKG_MGF1_SHA224, + }; + +enum class HardwareType : CK_HW_FEATURE_TYPE + { + MonotonicCounter = CKH_MONOTONIC_COUNTER, + Clock = CKH_CLOCK, + UserInterface = CKH_USER_INTERFACE, + VendorDefined = CKH_VENDOR_DEFINED, + }; + +enum class KeyType : CK_KEY_TYPE + { + Rsa = CKK_RSA, + Dsa = CKK_DSA, + Dh = CKK_DH, + Ecdsa = CKK_ECDSA, + Ec = CKK_EC, + X942Dh = CKK_X9_42_DH, + Kea = CKK_KEA, + GenericSecret = CKK_GENERIC_SECRET, + Rc2 = CKK_RC2, + Rc4 = CKK_RC4, + Des = CKK_DES, + Des2 = CKK_DES2, + Des3 = CKK_DES3, + Cast = CKK_CAST, + Cast3 = CKK_CAST3, + Cast5 = CKK_CAST5, + Cast128 = CKK_CAST128, + Rc5 = CKK_RC5, + Idea = CKK_IDEA, + Skipjack = CKK_SKIPJACK, + Baton = CKK_BATON, + Juniper = CKK_JUNIPER, + Cdmf = CKK_CDMF, + Aes = CKK_AES, + Blowfish = CKK_BLOWFISH, + Twofish = CKK_TWOFISH, + Securid = CKK_SECURID, + Hotp = CKK_HOTP, + Acti = CKK_ACTI, + Camellia = CKK_CAMELLIA, + Aria = CKK_ARIA, + Md5Hmac = CKK_MD5_HMAC, + Sha1Hmac = CKK_SHA_1_HMAC, + Ripemd128Hmac = CKK_RIPEMD128_HMAC, + Ripemd160Hmac = CKK_RIPEMD160_HMAC, + Sha256Hmac = CKK_SHA256_HMAC, + Sha384Hmac = CKK_SHA384_HMAC, + Sha512Hmac = CKK_SHA512_HMAC, + Sha224Hmac = CKK_SHA224_HMAC, + Seed = CKK_SEED, + Gostr3410 = CKK_GOSTR3410, + Gostr3411 = CKK_GOSTR3411, + Gost28147 = CKK_GOST28147, + VendorDefined = CKK_VENDOR_DEFINED, + }; + +enum class MechanismType : CK_MECHANISM_TYPE + { + RsaPkcsKeyPairGen = CKM_RSA_PKCS_KEY_PAIR_GEN, + RsaPkcs = CKM_RSA_PKCS, + Rsa9796 = CKM_RSA_9796, + RsaX509 = CKM_RSA_X_509, + Md2RsaPkcs = CKM_MD2_RSA_PKCS, + Md5RsaPkcs = CKM_MD5_RSA_PKCS, + Sha1RsaPkcs = CKM_SHA1_RSA_PKCS, + Ripemd128RsaPkcs = CKM_RIPEMD128_RSA_PKCS, + Ripemd160RsaPkcs = CKM_RIPEMD160_RSA_PKCS, + RsaPkcsOaep = CKM_RSA_PKCS_OAEP, + RsaX931KeyPairGen = CKM_RSA_X9_31_KEY_PAIR_GEN, + RsaX931 = CKM_RSA_X9_31, + Sha1RsaX931 = CKM_SHA1_RSA_X9_31, + RsaPkcsPss = CKM_RSA_PKCS_PSS, + Sha1RsaPkcsPss = CKM_SHA1_RSA_PKCS_PSS, + DsaKeyPairGen = CKM_DSA_KEY_PAIR_GEN, + Dsa = CKM_DSA, + DsaSha1 = CKM_DSA_SHA1, + DsaSha224 = CKM_DSA_SHA224, + DsaSha256 = CKM_DSA_SHA256, + DsaSha384 = CKM_DSA_SHA384, + DsaSha512 = CKM_DSA_SHA512, + DhPkcsKeyPairGen = CKM_DH_PKCS_KEY_PAIR_GEN, + DhPkcsDerive = CKM_DH_PKCS_DERIVE, + X942DhKeyPairGen = CKM_X9_42_DH_KEY_PAIR_GEN, + X942DhDerive = CKM_X9_42_DH_DERIVE, + X942DhHybridDerive = CKM_X9_42_DH_HYBRID_DERIVE, + X942MqvDerive = CKM_X9_42_MQV_DERIVE, + Sha256RsaPkcs = CKM_SHA256_RSA_PKCS, + Sha384RsaPkcs = CKM_SHA384_RSA_PKCS, + Sha512RsaPkcs = CKM_SHA512_RSA_PKCS, + Sha256RsaPkcsPss = CKM_SHA256_RSA_PKCS_PSS, + Sha384RsaPkcsPss = CKM_SHA384_RSA_PKCS_PSS, + Sha512RsaPkcsPss = CKM_SHA512_RSA_PKCS_PSS, + Sha224RsaPkcs = CKM_SHA224_RSA_PKCS, + Sha224RsaPkcsPss = CKM_SHA224_RSA_PKCS_PSS, + Sha512224 = CKM_SHA512_224, + Sha512224Hmac = CKM_SHA512_224_HMAC, + Sha512224HmacGeneral = CKM_SHA512_224_HMAC_GENERAL, + Sha512224KeyDerivation = CKM_SHA512_224_KEY_DERIVATION, + Sha512256 = CKM_SHA512_256, + Sha512256Hmac = CKM_SHA512_256_HMAC, + Sha512256HmacGeneral = CKM_SHA512_256_HMAC_GENERAL, + Sha512256KeyDerivation = CKM_SHA512_256_KEY_DERIVATION, + Sha512T = CKM_SHA512_T, + Sha512THmac = CKM_SHA512_T_HMAC, + Sha512THmacGeneral = CKM_SHA512_T_HMAC_GENERAL, + Sha512TKeyDerivation = CKM_SHA512_T_KEY_DERIVATION, + Rc2KeyGen = CKM_RC2_KEY_GEN, + Rc2Ecb = CKM_RC2_ECB, + Rc2Cbc = CKM_RC2_CBC, + Rc2Mac = CKM_RC2_MAC, + Rc2MacGeneral = CKM_RC2_MAC_GENERAL, + Rc2CbcPad = CKM_RC2_CBC_PAD, + Rc4KeyGen = CKM_RC4_KEY_GEN, + Rc4 = CKM_RC4, + DesKeyGen = CKM_DES_KEY_GEN, + DesEcb = CKM_DES_ECB, + DesCbc = CKM_DES_CBC, + DesMac = CKM_DES_MAC, + DesMacGeneral = CKM_DES_MAC_GENERAL, + DesCbcPad = CKM_DES_CBC_PAD, + Des2KeyGen = CKM_DES2_KEY_GEN, + Des3KeyGen = CKM_DES3_KEY_GEN, + Des3Ecb = CKM_DES3_ECB, + Des3Cbc = CKM_DES3_CBC, + Des3Mac = CKM_DES3_MAC, + Des3MacGeneral = CKM_DES3_MAC_GENERAL, + Des3CbcPad = CKM_DES3_CBC_PAD, + Des3CmacGeneral = CKM_DES3_CMAC_GENERAL, + Des3Cmac = CKM_DES3_CMAC, + CdmfKeyGen = CKM_CDMF_KEY_GEN, + CdmfEcb = CKM_CDMF_ECB, + CdmfCbc = CKM_CDMF_CBC, + CdmfMac = CKM_CDMF_MAC, + CdmfMacGeneral = CKM_CDMF_MAC_GENERAL, + CdmfCbcPad = CKM_CDMF_CBC_PAD, + DesOfb64 = CKM_DES_OFB64, + DesOfb8 = CKM_DES_OFB8, + DesCfb64 = CKM_DES_CFB64, + DesCfb8 = CKM_DES_CFB8, + Md2 = CKM_MD2, + Md2Hmac = CKM_MD2_HMAC, + Md2HmacGeneral = CKM_MD2_HMAC_GENERAL, + Md5 = CKM_MD5, + Md5Hmac = CKM_MD5_HMAC, + Md5HmacGeneral = CKM_MD5_HMAC_GENERAL, + Sha1 = CKM_SHA_1, + Sha1Hmac = CKM_SHA_1_HMAC, + Sha1HmacGeneral = CKM_SHA_1_HMAC_GENERAL, + Ripemd128 = CKM_RIPEMD128, + Ripemd128Hmac = CKM_RIPEMD128_HMAC, + Ripemd128HmacGeneral = CKM_RIPEMD128_HMAC_GENERAL, + Ripemd160 = CKM_RIPEMD160, + Ripemd160Hmac = CKM_RIPEMD160_HMAC, + Ripemd160HmacGeneral = CKM_RIPEMD160_HMAC_GENERAL, + Sha256 = CKM_SHA256, + Sha256Hmac = CKM_SHA256_HMAC, + Sha256HmacGeneral = CKM_SHA256_HMAC_GENERAL, + Sha224 = CKM_SHA224, + Sha224Hmac = CKM_SHA224_HMAC, + Sha224HmacGeneral = CKM_SHA224_HMAC_GENERAL, + Sha384 = CKM_SHA384, + Sha384Hmac = CKM_SHA384_HMAC, + Sha384HmacGeneral = CKM_SHA384_HMAC_GENERAL, + Sha512 = CKM_SHA512, + Sha512Hmac = CKM_SHA512_HMAC, + Sha512HmacGeneral = CKM_SHA512_HMAC_GENERAL, + SecuridKeyGen = CKM_SECURID_KEY_GEN, + Securid = CKM_SECURID, + HotpKeyGen = CKM_HOTP_KEY_GEN, + Hotp = CKM_HOTP, + Acti = CKM_ACTI, + ActiKeyGen = CKM_ACTI_KEY_GEN, + CastKeyGen = CKM_CAST_KEY_GEN, + CastEcb = CKM_CAST_ECB, + CastCbc = CKM_CAST_CBC, + CastMac = CKM_CAST_MAC, + CastMacGeneral = CKM_CAST_MAC_GENERAL, + CastCbcPad = CKM_CAST_CBC_PAD, + Cast3KeyGen = CKM_CAST3_KEY_GEN, + Cast3Ecb = CKM_CAST3_ECB, + Cast3Cbc = CKM_CAST3_CBC, + Cast3Mac = CKM_CAST3_MAC, + Cast3MacGeneral = CKM_CAST3_MAC_GENERAL, + Cast3CbcPad = CKM_CAST3_CBC_PAD, + Cast5KeyGen = CKM_CAST5_KEY_GEN, + Cast128KeyGen = CKM_CAST128_KEY_GEN, + Cast5Ecb = CKM_CAST5_ECB, + Cast128Ecb = CKM_CAST128_ECB, + Cast5Cbc = CKM_CAST5_CBC, + Cast128Cbc = CKM_CAST128_CBC, + Cast5Mac = CKM_CAST5_MAC, + Cast128Mac = CKM_CAST128_MAC, + Cast5MacGeneral = CKM_CAST5_MAC_GENERAL, + Cast128MacGeneral = CKM_CAST128_MAC_GENERAL, + Cast5CbcPad = CKM_CAST5_CBC_PAD, + Cast128CbcPad = CKM_CAST128_CBC_PAD, + Rc5KeyGen = CKM_RC5_KEY_GEN, + Rc5Ecb = CKM_RC5_ECB, + Rc5Cbc = CKM_RC5_CBC, + Rc5Mac = CKM_RC5_MAC, + Rc5MacGeneral = CKM_RC5_MAC_GENERAL, + Rc5CbcPad = CKM_RC5_CBC_PAD, + IdeaKeyGen = CKM_IDEA_KEY_GEN, + IdeaEcb = CKM_IDEA_ECB, + IdeaCbc = CKM_IDEA_CBC, + IdeaMac = CKM_IDEA_MAC, + IdeaMacGeneral = CKM_IDEA_MAC_GENERAL, + IdeaCbcPad = CKM_IDEA_CBC_PAD, + GenericSecretKeyGen = CKM_GENERIC_SECRET_KEY_GEN, + ConcatenateBaseAndKey = CKM_CONCATENATE_BASE_AND_KEY, + ConcatenateBaseAndData = CKM_CONCATENATE_BASE_AND_DATA, + ConcatenateDataAndBase = CKM_CONCATENATE_DATA_AND_BASE, + XorBaseAndData = CKM_XOR_BASE_AND_DATA, + ExtractKeyFromKey = CKM_EXTRACT_KEY_FROM_KEY, + Ssl3PreMasterKeyGen = CKM_SSL3_PRE_MASTER_KEY_GEN, + Ssl3MasterKeyDerive = CKM_SSL3_MASTER_KEY_DERIVE, + Ssl3KeyAndMacDerive = CKM_SSL3_KEY_AND_MAC_DERIVE, + Ssl3MasterKeyDeriveDh = CKM_SSL3_MASTER_KEY_DERIVE_DH, + TlsPreMasterKeyGen = CKM_TLS_PRE_MASTER_KEY_GEN, + TlsMasterKeyDerive = CKM_TLS_MASTER_KEY_DERIVE, + TlsKeyAndMacDerive = CKM_TLS_KEY_AND_MAC_DERIVE, + TlsMasterKeyDeriveDh = CKM_TLS_MASTER_KEY_DERIVE_DH, + TlsPrf = CKM_TLS_PRF, + Ssl3Md5Mac = CKM_SSL3_MD5_MAC, + Ssl3Sha1Mac = CKM_SSL3_SHA1_MAC, + Md5KeyDerivation = CKM_MD5_KEY_DERIVATION, + Md2KeyDerivation = CKM_MD2_KEY_DERIVATION, + Sha1KeyDerivation = CKM_SHA1_KEY_DERIVATION, + Sha256KeyDerivation = CKM_SHA256_KEY_DERIVATION, + Sha384KeyDerivation = CKM_SHA384_KEY_DERIVATION, + Sha512KeyDerivation = CKM_SHA512_KEY_DERIVATION, + Sha224KeyDerivation = CKM_SHA224_KEY_DERIVATION, + PbeMd2DesCbc = CKM_PBE_MD2_DES_CBC, + PbeMd5DesCbc = CKM_PBE_MD5_DES_CBC, + PbeMd5CastCbc = CKM_PBE_MD5_CAST_CBC, + PbeMd5Cast3Cbc = CKM_PBE_MD5_CAST3_CBC, + PbeMd5Cast5Cbc = CKM_PBE_MD5_CAST5_CBC, + PbeMd5Cast128Cbc = CKM_PBE_MD5_CAST128_CBC, + PbeSha1Cast5Cbc = CKM_PBE_SHA1_CAST5_CBC, + PbeSha1Cast128Cbc = CKM_PBE_SHA1_CAST128_CBC, + PbeSha1Rc4128 = CKM_PBE_SHA1_RC4_128, + PbeSha1Rc440 = CKM_PBE_SHA1_RC4_40, + PbeSha1Des3EdeCbc = CKM_PBE_SHA1_DES3_EDE_CBC, + PbeSha1Des2EdeCbc = CKM_PBE_SHA1_DES2_EDE_CBC, + PbeSha1Rc2128Cbc = CKM_PBE_SHA1_RC2_128_CBC, + PbeSha1Rc240Cbc = CKM_PBE_SHA1_RC2_40_CBC, + Pkcs5Pbkd2 = CKM_PKCS5_PBKD2, + PbaSha1WithSha1Hmac = CKM_PBA_SHA1_WITH_SHA1_HMAC, + WtlsPreMasterKeyGen = CKM_WTLS_PRE_MASTER_KEY_GEN, + WtlsMasterKeyDerive = CKM_WTLS_MASTER_KEY_DERIVE, + WtlsMasterKeyDeriveDhEcc = CKM_WTLS_MASTER_KEY_DERIVE_DH_ECC, + WtlsPrf = CKM_WTLS_PRF, + WtlsServerKeyAndMacDerive = CKM_WTLS_SERVER_KEY_AND_MAC_DERIVE, + WtlsClientKeyAndMacDerive = CKM_WTLS_CLIENT_KEY_AND_MAC_DERIVE, + Tls10MacServer = CKM_TLS10_MAC_SERVER, + Tls10MacClient = CKM_TLS10_MAC_CLIENT, + Tls12Mac = CKM_TLS12_MAC, + Tls12Kdf = CKM_TLS12_KDF, + Tls12MasterKeyDerive = CKM_TLS12_MASTER_KEY_DERIVE, + Tls12KeyAndMacDerive = CKM_TLS12_KEY_AND_MAC_DERIVE, + Tls12MasterKeyDeriveDh = CKM_TLS12_MASTER_KEY_DERIVE_DH, + Tls12KeySafeDerive = CKM_TLS12_KEY_SAFE_DERIVE, + TlsMac = CKM_TLS_MAC, + TlsKdf = CKM_TLS_KDF, + KeyWrapLynks = CKM_KEY_WRAP_LYNKS, + KeyWrapSetOaep = CKM_KEY_WRAP_SET_OAEP, + CmsSig = CKM_CMS_SIG, + KipDerive = CKM_KIP_DERIVE, + KipWrap = CKM_KIP_WRAP, + KipMac = CKM_KIP_MAC, + CamelliaKeyGen = CKM_CAMELLIA_KEY_GEN, + CamelliaEcb = CKM_CAMELLIA_ECB, + CamelliaCbc = CKM_CAMELLIA_CBC, + CamelliaMac = CKM_CAMELLIA_MAC, + CamelliaMacGeneral = CKM_CAMELLIA_MAC_GENERAL, + CamelliaCbcPad = CKM_CAMELLIA_CBC_PAD, + CamelliaEcbEncryptData = CKM_CAMELLIA_ECB_ENCRYPT_DATA, + CamelliaCbcEncryptData = CKM_CAMELLIA_CBC_ENCRYPT_DATA, + CamelliaCtr = CKM_CAMELLIA_CTR, + AriaKeyGen = CKM_ARIA_KEY_GEN, + AriaEcb = CKM_ARIA_ECB, + AriaCbc = CKM_ARIA_CBC, + AriaMac = CKM_ARIA_MAC, + AriaMacGeneral = CKM_ARIA_MAC_GENERAL, + AriaCbcPad = CKM_ARIA_CBC_PAD, + AriaEcbEncryptData = CKM_ARIA_ECB_ENCRYPT_DATA, + AriaCbcEncryptData = CKM_ARIA_CBC_ENCRYPT_DATA, + SeedKeyGen = CKM_SEED_KEY_GEN, + SeedEcb = CKM_SEED_ECB, + SeedCbc = CKM_SEED_CBC, + SeedMac = CKM_SEED_MAC, + SeedMacGeneral = CKM_SEED_MAC_GENERAL, + SeedCbcPad = CKM_SEED_CBC_PAD, + SeedEcbEncryptData = CKM_SEED_ECB_ENCRYPT_DATA, + SeedCbcEncryptData = CKM_SEED_CBC_ENCRYPT_DATA, + SkipjackKeyGen = CKM_SKIPJACK_KEY_GEN, + SkipjackEcb64 = CKM_SKIPJACK_ECB64, + SkipjackCbc64 = CKM_SKIPJACK_CBC64, + SkipjackOfb64 = CKM_SKIPJACK_OFB64, + SkipjackCfb64 = CKM_SKIPJACK_CFB64, + SkipjackCfb32 = CKM_SKIPJACK_CFB32, + SkipjackCfb16 = CKM_SKIPJACK_CFB16, + SkipjackCfb8 = CKM_SKIPJACK_CFB8, + SkipjackWrap = CKM_SKIPJACK_WRAP, + SkipjackPrivateWrap = CKM_SKIPJACK_PRIVATE_WRAP, + SkipjackRelayx = CKM_SKIPJACK_RELAYX, + KeaKeyPairGen = CKM_KEA_KEY_PAIR_GEN, + KeaKeyDerive = CKM_KEA_KEY_DERIVE, + KeaDerive = CKM_KEA_DERIVE, + FortezzaTimestamp = CKM_FORTEZZA_TIMESTAMP, + BatonKeyGen = CKM_BATON_KEY_GEN, + BatonEcb128 = CKM_BATON_ECB128, + BatonEcb96 = CKM_BATON_ECB96, + BatonCbc128 = CKM_BATON_CBC128, + BatonCounter = CKM_BATON_COUNTER, + BatonShuffle = CKM_BATON_SHUFFLE, + BatonWrap = CKM_BATON_WRAP, + EcdsaKeyPairGen = CKM_ECDSA_KEY_PAIR_GEN, + EcKeyPairGen = CKM_EC_KEY_PAIR_GEN, + Ecdsa = CKM_ECDSA, + EcdsaSha1 = CKM_ECDSA_SHA1, + EcdsaSha224 = CKM_ECDSA_SHA224, + EcdsaSha256 = CKM_ECDSA_SHA256, + EcdsaSha384 = CKM_ECDSA_SHA384, + EcdsaSha512 = CKM_ECDSA_SHA512, + Ecdh1Derive = CKM_ECDH1_DERIVE, + Ecdh1CofactorDerive = CKM_ECDH1_COFACTOR_DERIVE, + EcmqvDerive = CKM_ECMQV_DERIVE, + EcdhAesKeyWrap = CKM_ECDH_AES_KEY_WRAP, + RsaAesKeyWrap = CKM_RSA_AES_KEY_WRAP, + JuniperKeyGen = CKM_JUNIPER_KEY_GEN, + JuniperEcb128 = CKM_JUNIPER_ECB128, + JuniperCbc128 = CKM_JUNIPER_CBC128, + JuniperCounter = CKM_JUNIPER_COUNTER, + JuniperShuffle = CKM_JUNIPER_SHUFFLE, + JuniperWrap = CKM_JUNIPER_WRAP, + Fasthash = CKM_FASTHASH, + AesKeyGen = CKM_AES_KEY_GEN, + AesEcb = CKM_AES_ECB, + AesCbc = CKM_AES_CBC, + AesMac = CKM_AES_MAC, + AesMacGeneral = CKM_AES_MAC_GENERAL, + AesCbcPad = CKM_AES_CBC_PAD, + AesCtr = CKM_AES_CTR, + AesGcm = CKM_AES_GCM, + AesCcm = CKM_AES_CCM, + AesCts = CKM_AES_CTS, + AesCmac = CKM_AES_CMAC, + AesCmacGeneral = CKM_AES_CMAC_GENERAL, + AesXcbcMac = CKM_AES_XCBC_MAC, + AesXcbcMac96 = CKM_AES_XCBC_MAC_96, + AesGmac = CKM_AES_GMAC, + BlowfishKeyGen = CKM_BLOWFISH_KEY_GEN, + BlowfishCbc = CKM_BLOWFISH_CBC, + TwofishKeyGen = CKM_TWOFISH_KEY_GEN, + TwofishCbc = CKM_TWOFISH_CBC, + BlowfishCbcPad = CKM_BLOWFISH_CBC_PAD, + TwofishCbcPad = CKM_TWOFISH_CBC_PAD, + DesEcbEncryptData = CKM_DES_ECB_ENCRYPT_DATA, + DesCbcEncryptData = CKM_DES_CBC_ENCRYPT_DATA, + Des3EcbEncryptData = CKM_DES3_ECB_ENCRYPT_DATA, + Des3CbcEncryptData = CKM_DES3_CBC_ENCRYPT_DATA, + AesEcbEncryptData = CKM_AES_ECB_ENCRYPT_DATA, + AesCbcEncryptData = CKM_AES_CBC_ENCRYPT_DATA, + Gostr3410KeyPairGen = CKM_GOSTR3410_KEY_PAIR_GEN, + Gostr3410 = CKM_GOSTR3410, + Gostr3410WithGostr3411 = CKM_GOSTR3410_WITH_GOSTR3411, + Gostr3410KeyWrap = CKM_GOSTR3410_KEY_WRAP, + Gostr3410Derive = CKM_GOSTR3410_DERIVE, + Gostr3411 = CKM_GOSTR3411, + Gostr3411Hmac = CKM_GOSTR3411_HMAC, + Gost28147KeyGen = CKM_GOST28147_KEY_GEN, + Gost28147Ecb = CKM_GOST28147_ECB, + Gost28147 = CKM_GOST28147, + Gost28147Mac = CKM_GOST28147_MAC, + Gost28147KeyWrap = CKM_GOST28147_KEY_WRAP, + DsaParameterGen = CKM_DSA_PARAMETER_GEN, + DhPkcsParameterGen = CKM_DH_PKCS_PARAMETER_GEN, + X942DhParameterGen = CKM_X9_42_DH_PARAMETER_GEN, + DsaProbablisticParameterGen = CKM_DSA_PROBABLISTIC_PARAMETER_GEN, + DsaShaweTaylorParameterGen = CKM_DSA_SHAWE_TAYLOR_PARAMETER_GEN, + AesOfb = CKM_AES_OFB, + AesCfb64 = CKM_AES_CFB64, + AesCfb8 = CKM_AES_CFB8, + AesCfb128 = CKM_AES_CFB128, + AesCfb1 = CKM_AES_CFB1, + AesKeyWrap = CKM_AES_KEY_WRAP, + AesKeyWrapPad = CKM_AES_KEY_WRAP_PAD, + RsaPkcsTpm11 = CKM_RSA_PKCS_TPM_1_1, + RsaPkcsOaepTpm11 = CKM_RSA_PKCS_OAEP_TPM_1_1, + VendorDefined = CKM_VENDOR_DEFINED, + }; + +enum class Notification : CK_NOTIFICATION + { + Surrender = CKN_SURRENDER, + OtpChanged = CKN_OTP_CHANGED, + }; + +enum class ObjectClass : CK_OBJECT_CLASS + { + Data = CKO_DATA, + Certificate = CKO_CERTIFICATE, + PublicKey = CKO_PUBLIC_KEY, + PrivateKey = CKO_PRIVATE_KEY, + SecretKey = CKO_SECRET_KEY, + HwFeature = CKO_HW_FEATURE, + DomainParameters = CKO_DOMAIN_PARAMETERS, + Mechanism = CKO_MECHANISM, + OtpKey = CKO_OTP_KEY, + VendorDefined = CKO_VENDOR_DEFINED, + }; + +enum class PseudoRandom : CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE + { + Pkcs5Pbkd2HmacSha1 = CKP_PKCS5_PBKD2_HMAC_SHA1, + Pkcs5Pbkd2HmacGostr3411 = CKP_PKCS5_PBKD2_HMAC_GOSTR3411, + Pkcs5Pbkd2HmacSha224 = CKP_PKCS5_PBKD2_HMAC_SHA224, + Pkcs5Pbkd2HmacSha256 = CKP_PKCS5_PBKD2_HMAC_SHA256, + Pkcs5Pbkd2HmacSha384 = CKP_PKCS5_PBKD2_HMAC_SHA384, + Pkcs5Pbkd2HmacSha512 = CKP_PKCS5_PBKD2_HMAC_SHA512, + Pkcs5Pbkd2HmacSha512224 = CKP_PKCS5_PBKD2_HMAC_SHA512_224, + Pkcs5Pbkd2HmacSha512256 = CKP_PKCS5_PBKD2_HMAC_SHA512_256, + }; + +enum class SessionState : CK_STATE + { + RoPublicSession = CKS_RO_PUBLIC_SESSION, + RoUserFunctions = CKS_RO_USER_FUNCTIONS, + RwPublicSession = CKS_RW_PUBLIC_SESSION, + RwUserFunctions = CKS_RW_USER_FUNCTIONS, + RwSoFunctions = CKS_RW_SO_FUNCTIONS, + }; + +enum class ReturnValue : CK_RV + { + OK = CKR_OK, + Cancel = CKR_CANCEL, + HostMemory = CKR_HOST_MEMORY, + SlotIdInvalid = CKR_SLOT_ID_INVALID, + GeneralError = CKR_GENERAL_ERROR, + FunctionFailed = CKR_FUNCTION_FAILED, + ArgumentsBad = CKR_ARGUMENTS_BAD, + NoEvent = CKR_NO_EVENT, + NeedToCreateThreads = CKR_NEED_TO_CREATE_THREADS, + CantLock = CKR_CANT_LOCK, + AttributeReadOnly = CKR_ATTRIBUTE_READ_ONLY, + AttributeSensitive = CKR_ATTRIBUTE_SENSITIVE, + AttributeTypeInvalid = CKR_ATTRIBUTE_TYPE_INVALID, + AttributeValueInvalid = CKR_ATTRIBUTE_VALUE_INVALID, + ActionProhibited = CKR_ACTION_PROHIBITED, + DataInvalid = CKR_DATA_INVALID, + DataLenRange = CKR_DATA_LEN_RANGE, + DeviceError = CKR_DEVICE_ERROR, + DeviceMemory = CKR_DEVICE_MEMORY, + DeviceRemoved = CKR_DEVICE_REMOVED, + EncryptedDataInvalid = CKR_ENCRYPTED_DATA_INVALID, + EncryptedDataLenRange = CKR_ENCRYPTED_DATA_LEN_RANGE, + FunctionCanceled = CKR_FUNCTION_CANCELED, + FunctionNotParallel = CKR_FUNCTION_NOT_PARALLEL, + FunctionNotSupported = CKR_FUNCTION_NOT_SUPPORTED, + KeyHandleInvalid = CKR_KEY_HANDLE_INVALID, + KeySizeRange = CKR_KEY_SIZE_RANGE, + KeyTypeInconsistent = CKR_KEY_TYPE_INCONSISTENT, + KeyNotNeeded = CKR_KEY_NOT_NEEDED, + KeyChanged = CKR_KEY_CHANGED, + KeyNeeded = CKR_KEY_NEEDED, + KeyIndigestible = CKR_KEY_INDIGESTIBLE, + KeyFunctionNotPermitted = CKR_KEY_FUNCTION_NOT_PERMITTED, + KeyNotWrappable = CKR_KEY_NOT_WRAPPABLE, + KeyUnextractable = CKR_KEY_UNEXTRACTABLE, + MechanismInvalid = CKR_MECHANISM_INVALID, + MechanismParamInvalid = CKR_MECHANISM_PARAM_INVALID, + ObjectHandleInvalid = CKR_OBJECT_HANDLE_INVALID, + OperationActive = CKR_OPERATION_ACTIVE, + OperationNotInitialized = CKR_OPERATION_NOT_INITIALIZED, + PinIncorrect = CKR_PIN_INCORRECT, + PinInvalid = CKR_PIN_INVALID, + PinLenRange = CKR_PIN_LEN_RANGE, + PinExpired = CKR_PIN_EXPIRED, + PinLocked = CKR_PIN_LOCKED, + SessionClosed = CKR_SESSION_CLOSED, + SessionCount = CKR_SESSION_COUNT, + SessionHandleInvalid = CKR_SESSION_HANDLE_INVALID, + SessionParallelNotSupported = CKR_SESSION_PARALLEL_NOT_SUPPORTED, + SessionReadOnly = CKR_SESSION_READ_ONLY, + SessionExists = CKR_SESSION_EXISTS, + SessionReadOnlyExists = CKR_SESSION_READ_ONLY_EXISTS, + SessionReadWriteSoExists = CKR_SESSION_READ_WRITE_SO_EXISTS, + SignatureInvalid = CKR_SIGNATURE_INVALID, + SignatureLenRange = CKR_SIGNATURE_LEN_RANGE, + TemplateIncomplete = CKR_TEMPLATE_INCOMPLETE, + TemplateInconsistent = CKR_TEMPLATE_INCONSISTENT, + TokenNotPresent = CKR_TOKEN_NOT_PRESENT, + TokenNotRecognized = CKR_TOKEN_NOT_RECOGNIZED, + TokenWriteProtected = CKR_TOKEN_WRITE_PROTECTED, + UnwrappingKeyHandleInvalid = CKR_UNWRAPPING_KEY_HANDLE_INVALID, + UnwrappingKeySizeRange = CKR_UNWRAPPING_KEY_SIZE_RANGE, + UnwrappingKeyTypeInconsistent = CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT, + UserAlreadyLoggedIn = CKR_USER_ALREADY_LOGGED_IN, + UserNotLoggedIn = CKR_USER_NOT_LOGGED_IN, + UserPinNotInitialized = CKR_USER_PIN_NOT_INITIALIZED, + UserTypeInvalid = CKR_USER_TYPE_INVALID, + UserAnotherAlreadyLoggedIn = CKR_USER_ANOTHER_ALREADY_LOGGED_IN, + UserTooManyTypes = CKR_USER_TOO_MANY_TYPES, + WrappedKeyInvalid = CKR_WRAPPED_KEY_INVALID, + WrappedKeyLenRange = CKR_WRAPPED_KEY_LEN_RANGE, + WrappingKeyHandleInvalid = CKR_WRAPPING_KEY_HANDLE_INVALID, + WrappingKeySizeRange = CKR_WRAPPING_KEY_SIZE_RANGE, + WrappingKeyTypeInconsistent = CKR_WRAPPING_KEY_TYPE_INCONSISTENT, + RandomSeedNotSupported = CKR_RANDOM_SEED_NOT_SUPPORTED, + RandomNoRng = CKR_RANDOM_NO_RNG, + DomainParamsInvalid = CKR_DOMAIN_PARAMS_INVALID, + CurveNotSupported = CKR_CURVE_NOT_SUPPORTED, + BufferTooSmall = CKR_BUFFER_TOO_SMALL, + SavedStateInvalid = CKR_SAVED_STATE_INVALID, + InformationSensitive = CKR_INFORMATION_SENSITIVE, + StateUnsaveable = CKR_STATE_UNSAVEABLE, + CryptokiNotInitialized = CKR_CRYPTOKI_NOT_INITIALIZED, + CryptokiAlreadyInitialized = CKR_CRYPTOKI_ALREADY_INITIALIZED, + MutexBad = CKR_MUTEX_BAD, + MutexNotLocked = CKR_MUTEX_NOT_LOCKED, + NewPinMode = CKR_NEW_PIN_MODE, + NextOtp = CKR_NEXT_OTP, + ExceededMaxIterations = CKR_EXCEEDED_MAX_ITERATIONS, + FipsSelfTestFailed = CKR_FIPS_SELF_TEST_FAILED, + LibraryLoadFailed = CKR_LIBRARY_LOAD_FAILED, + PinTooWeak = CKR_PIN_TOO_WEAK, + PublicKeyInvalid = CKR_PUBLIC_KEY_INVALID, + FunctionRejected = CKR_FUNCTION_REJECTED, + VendorDefined = CKR_VENDOR_DEFINED, + }; + +enum class UserType : CK_USER_TYPE + { + SO = CKU_SO, + User = CKU_USER, + ContextSpecific = CKU_CONTEXT_SPECIFIC, + }; + +enum class PublicPointEncoding : uint32_t + { + Raw, + Der + }; + +using FunctionListPtr = CK_FUNCTION_LIST_PTR; +using VoidPtr = CK_VOID_PTR; +using C_InitializeArgs = CK_C_INITIALIZE_ARGS; +using CreateMutex = CK_CREATEMUTEX; +using DestroyMutex = CK_DESTROYMUTEX; +using LockMutex = CK_LOCKMUTEX; +using UnlockMutex = CK_UNLOCKMUTEX; +using Flags = CK_FLAGS; +using Info = CK_INFO; +using Bbool = CK_BBOOL; +using SlotId = CK_SLOT_ID; +using Ulong = CK_ULONG; +using SlotInfo = CK_SLOT_INFO; +using TokenInfo = CK_TOKEN_INFO; +using Mechanism = CK_MECHANISM; +using MechanismInfo = CK_MECHANISM_INFO; +using Utf8Char = CK_UTF8CHAR; +using Notify = CK_NOTIFY; +using SessionHandle = CK_SESSION_HANDLE; +using SessionInfo = CK_SESSION_INFO; +using Attribute = CK_ATTRIBUTE; +using ObjectHandle = CK_OBJECT_HANDLE; +using Byte = CK_BYTE; +using RsaPkcsOaepParams = CK_RSA_PKCS_OAEP_PARAMS; +using RsaPkcsPssParams = CK_RSA_PKCS_PSS_PARAMS; +using Ecdh1DeriveParams = CK_ECDH1_DERIVE_PARAMS; +using Date = CK_DATE; + +BOTAN_DLL extern ReturnValue* ThrowException; + +const Bbool True = CK_TRUE; +const Bbool False = CK_FALSE; + +inline Flags flags(Flag flags) + { + return static_cast(flags); + } + +class Slot; + +/** +* Initializes a token +* @param slot The slot with the attached token that should be initialized +* @param label The token label +* @param so_pin PIN of the security officer. Will be set if the token is uninitialized other this has to be the current SO_PIN +* @param pin The user PIN that will be set +*/ +BOTAN_DLL void initialize_token(Slot& slot, const std::string& label, const secure_string& so_pin, + const secure_string& pin); + +/** +* Change PIN with old PIN to new PIN +* @param slot The slot with the attached token +* @param old_pin The old user PIN +* @param new_pin The new user PIN +*/ + +BOTAN_DLL void change_pin(Slot& slot, const secure_string& old_pin, const secure_string& new_pin); + +/** +* Change SO_PIN with old SO_PIN to new SO_PIN +* @param slot The slot with the attached token +* @param old_so_pin The old SO_PIN +* @param new_so_pin The new SO_PIN +*/ +BOTAN_DLL void change_so_pin(Slot& slot, const secure_string& old_so_pin, const secure_string& new_so_pin); + +/** +* Sets user PIN with SO_PIN +* @param slot The slot with the attached token +* @param so_pin PIN of the security officer +* @param pin The user PIN that should be set +*/ +BOTAN_DLL void set_pin(Slot& slot, const secure_string& so_pin, const secure_string& pin); + +/// Provides access to all PKCS#11 functions +class BOTAN_DLL LowLevel + { + public: + + /// @param ptr the functon list pointer to use. Can be retrieved via `LowLevel::C_GetFunctionList` + explicit LowLevel(FunctionListPtr ptr); + + ~LowLevel() BOTAN_NOEXCEPT; + + /****************************** General purpose functions ******************************/ + + /** + * C_Initialize initializes the Cryptoki library. + * @param init_args if this is not nullptr, it gets cast to (`C_InitializeArgs`) and dereferenced + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li CantLock \li CryptokiAlreadyInitialized + * \li FunctionFailed \li GeneralError \li HostMemory + * \li NeedToCreateThreads \li OK + * @return true on success, false otherwise + */ + bool C_Initialize(VoidPtr init_args, + ReturnValue* return_value = ThrowException) const; + + /** + * C_Finalize indicates that an application is done with the Cryptoki library. + * @param reserved reserved. Should be nullptr + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li CryptokiNotInitialized \li FunctionFailed + * \li GeneralError \li HostMemory \li OK + * @return true on success, false otherwise + */ + bool C_Finalize(VoidPtr reserved, + ReturnValue* return_value = ThrowException) const; + + /** + * C_GetInfo returns general information about Cryptoki. + * @param info_ptr location that receives information + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li CryptokiNotInitialized \li FunctionFailed + * \li GeneralError \li HostMemory \li OK + * @return true on success, false otherwise + */ + bool C_GetInfo(Info* info_ptr, + ReturnValue* return_value = ThrowException) const; + + /** + * C_GetFunctionList returns the function list. + * @param pkcs11_module The PKCS#11 module + * @param function_list_ptr_ptr receives pointer to function list + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li FunctionFailed \li GeneralError + * \li HostMemory \li OK + * @return true on success, false otherwise + */ + static bool C_GetFunctionList(Dynamically_Loaded_Library& pkcs11_module, FunctionListPtr* function_list_ptr_ptr, + ReturnValue* return_value = ThrowException); + + /****************************** Slot and token management functions ******************************/ + + /** + * C_GetSlotList obtains a list of slots in the system. + * @param token_present only slots with tokens + * @param slot_list_ptr receives array of slot IDs + * @param count_ptr receives number of slots + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized + * \li FunctionFailed \li GeneralError \li HostMemory + * \li OK + * @return true on success, false otherwise + */ + bool C_GetSlotList(Bbool token_present, + SlotId* slot_list_ptr, + Ulong* count_ptr, + ReturnValue* return_value = ThrowException) const; + + /** + * C_GetSlotList obtains a list of slots in the system. + * @param token_present only slots with tokens + * @param slot_ids receives vector of slot IDs + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized + * \li FunctionFailed \li GeneralError \li HostMemory + * \li OK + * @return true on success, false otherwise + */ + bool C_GetSlotList(bool token_present, + std::vector& slot_ids, + ReturnValue* return_value = ThrowException) const; + + /** + * C_GetSlotInfo obtains information about a particular slot in the system. + * @param slot_id the ID of the slot + * @param info_ptr receives the slot information + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError + * \li FunctionFailed \li GeneralError \li HostMemory + * \li OK \li SlotIdInvalid + * @return true on success, false otherwise + */ + bool C_GetSlotInfo(SlotId slot_id, + SlotInfo* info_ptr, + ReturnValue* return_value = ThrowException) const; + + /** + * C_GetTokenInfo obtains information about a particular token in the system. + * @param slot_id ID of the token's slot + * @param info_ptr receives the token information + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory + * \li DeviceRemoved \li FunctionFailed \li GeneralError + * \li HostMemory \li OK \li SlotIdInvalid + * \li TokenNotPresent \li TokenNotRecognized \li ArgumentsBad + * @return true on success, false otherwise + */ + bool C_GetTokenInfo(SlotId slot_id, + TokenInfo* info_ptr, + ReturnValue* return_value = ThrowException) const; + + /** + * C_WaitForSlotEvent waits for a slot event (token insertion, removal, etc.) to occur. + * @param flags blocking/nonblocking flag + * @param slot_ptr location that receives the slot ID + * @param reserved reserved. Should be NULL_PTR + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li CryptokiNotInitialized \li FunctionFailed + * \li GeneralError \li HostMemory \li NoEvent + * \li OK + * @return true on success, false otherwise + */ + bool C_WaitForSlotEvent(Flags flags, + SlotId* slot_ptr, + VoidPtr reserved, + ReturnValue* return_value = ThrowException) const; + + /** + * C_GetMechanismList obtains a list of mechanism types supported by a token. + * @param slot_id ID of token's slot + * @param mechanism_list_ptr gets mech. array + * @param count_ptr gets # of mechs. + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li BufferTooSmall \li CryptokiNotInitialized \li DeviceError + * \li DeviceMemory \li DeviceRemoved \li FunctionFailed + * \li GeneralError \li HostMemory \li OK + * \li SlotIdInvalid \li TokenNotPresent \li TokenNotRecognized + * \li ArgumentsBad + * @return true on success, false otherwise + */ + bool C_GetMechanismList(SlotId slot_id, + MechanismType* mechanism_list_ptr, + Ulong* count_ptr, + ReturnValue* return_value = ThrowException) const; + + /** + * C_GetMechanismList obtains a list of mechanism types supported by a token. + * @param slot_id ID of token's slot + * @param mechanisms receives vector of supported mechanisms + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li BufferTooSmall \li CryptokiNotInitialized \li DeviceError + * \li DeviceMemory \li DeviceRemoved \li FunctionFailed + * \li GeneralError \li HostMemory \li OK + * \li SlotIdInvalid \li TokenNotPresent \li TokenNotRecognized + * \li ArgumentsBad + * @return true on success, false otherwise + */ + bool C_GetMechanismList(SlotId slot_id, + std::vector& mechanisms, + ReturnValue* return_value = ThrowException) const; + + /** + * C_GetMechanismInfo obtains information about a particular mechanism possibly supported by a token. + * @param slot_id ID of the token's slot + * @param type type of mechanism + * @param info_ptr receives mechanism info + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory + * \li DeviceRemoved \li FunctionFailed \li GeneralError + * \li HostMemory \li MechanismInvalid \li OK + * \li SlotIdInvalid \li TokenNotPresent \li TokenNotRecognized + * \li ArgumentsBad + * @return true on success, false otherwise + */ + bool C_GetMechanismInfo(SlotId slot_id, + MechanismType type, + MechanismInfo* info_ptr, + ReturnValue* return_value = ThrowException) const; + + /** + * C_InitToken initializes a token. + * @param slot_id ID of the token's slot + * @param so_pin_ptr the SO's initial PIN + * @param so_pin_len length in bytes of the SO_PIN + * @param label_ptr 32-byte token label (blank padded) + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory + * \li DeviceRemoved \li FunctionCanceled \li FunctionFailed + * \li GeneralError \li HostMemory \li OK + * \li PinIncorrect \li PinLocked \li SessionExists + * \li SlotIdInvalid \li TokenNotPresent \li TokenNotRecognized + * \li TokenWriteProtected \li ArgumentsBad + * @return true on success, false otherwise + */ + bool C_InitToken(SlotId slot_id, + Utf8Char* so_pin_ptr, + Ulong so_pin_len, + Utf8Char* label_ptr, + ReturnValue* return_value = ThrowException) const; + + /** + * C_InitToken initializes a token. + * @param slot_id ID of the token's slot + * @param so_pin the SO's initial PIN + * @param label token label (at max 32 bytes long) + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory + * \li DeviceRemoved \li FunctionCanceled \li FunctionFailed + * \li GeneralError \li HostMemory \li OK + * \li PinIncorrect \li PinLocked \li SessionExists + * \li SlotIdInvalid \li TokenNotPresent \li TokenNotRecognized + * \li TokenWriteProtected \li ArgumentsBad + * @return true on success, false otherwise + */ + template + bool C_InitToken(SlotId slot_id, + const std::vector& so_pin, + const std::string& label, + ReturnValue* return_value = ThrowException) const + { + std::string padded_label = label; + if(label.size() < 32) + { + padded_label.insert(padded_label.end(), 32 - label.size(), ' '); + } + + return C_InitToken(slot_id, reinterpret_cast< Utf8Char* >(const_cast< byte* >(so_pin.data())), + so_pin.size(), reinterpret_cast< Utf8Char* >(const_cast< char* >(padded_label.c_str())), return_value); + } + + /** + * C_InitPIN initializes the normal user's PIN. + * @param session the session's handle + * @param pin_ptr the normal user's PIN + * @param pin_len length in bytes of the PIN + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory + * \li DeviceRemoved \li FunctionCanceled \li FunctionFailed + * \li GeneralError \li HostMemory \li OK + * \li PinInvalid \li PinLenRange \li SessionClosed + * \li SessionReadOnly \li SessionHandleInvalid \li TokenWriteProtected + * \li UserNotLoggedIn \li ArgumentsBad + * @return true on success, false otherwise + */ + bool C_InitPIN(SessionHandle session, + Utf8Char* pin_ptr, + Ulong pin_len, + ReturnValue* return_value = ThrowException) const; + + /** + * C_InitPIN initializes the normal user's PIN. + * @param session the session's handle + * @param pin the normal user's PIN + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory + * \li DeviceRemoved \li FunctionCanceled \li FunctionFailed + * \li GeneralError \li HostMemory \li OK + * \li PinInvalid \li PinLenRange \li SessionClosed + * \li SessionReadOnly \li SessionHandleInvalid \li TokenWriteProtected + * \li UserNotLoggedIn \li ArgumentsBad + * @return true on success, false otherwise + */ + template + bool C_InitPIN(SessionHandle session, + const std::vector& pin, + ReturnValue* return_value = ThrowException) const + { + return C_InitPIN(session, reinterpret_cast< Utf8Char* >(const_cast< byte* >(pin.data())), pin.size(), return_value); + } + + /** + * C_SetPIN modifies the PIN of the user who is logged in. + * @param session the session's handle + * @param old_pin_ptr the old PIN + * @param old_len length of the old PIN + * @param new_pin_ptr the new PIN + * @param new_len length of the new PIN + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory + * \li DeviceRemoved \li FunctionCanceled \li FunctionFailed + * \li GeneralError \li HostMemory \li OK + * \li PinIncorrect \li PinInvalid \li PinLenRange + * \li PinLocked \li SessionClosed \li SessionHandleInvalid + * \li SessionReadOnly \li TokenWriteProtected \li ArgumentsBad + * @return true on success, false otherwise + */ + bool C_SetPIN(SessionHandle session, + Utf8Char* old_pin_ptr, + Ulong old_len, + Utf8Char* new_pin_ptr, + Ulong new_len, + ReturnValue* return_value = ThrowException) const; + + /** + * C_SetPIN modifies the PIN of the user who is logged in. + * @param session the session's handle + * @param old_pin the old PIN + * @param new_pin the new PIN + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory + * \li DeviceRemoved \li FunctionCanceled \li FunctionFailed + * \li GeneralError \li HostMemory \li OK + * \li PinIncorrect \li PinInvalid \li PinLenRange + * \li PinLocked \li SessionClosed \li SessionHandleInvalid + * \li SessionReadOnly \li TokenWriteProtected \li ArgumentsBad + * @return true on success, false otherwise + */ + template + bool C_SetPIN(SessionHandle session, + const std::vector& old_pin, + const std::vector& new_pin, + ReturnValue* return_value = ThrowException) const + { + return C_SetPIN(session, + reinterpret_cast< Utf8Char* >(const_cast< byte* >(old_pin.data())), old_pin.size(), + reinterpret_cast< Utf8Char* >(const_cast< byte* >(new_pin.data())), new_pin.size(), + return_value); + } + + + /****************************** Session management ******************************/ + + /** + * C_OpenSession opens a session between an application and a token. + * @param slot_id the slot's ID + * @param flags from CK_SESSION_INFO + * @param application passed to callback + * @param notify callback function + * @param session_ptr gets session handle + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory + * \li DeviceRemoved \li FunctionFailed \li GeneralError + * \li HostMemory \li OK \li SessionCount + * \li SessionParallelNotSupported \li SessionReadWriteSoExists \li SlotIdInvalid + * \li TokenNotPresent \li TokenNotRecognized \li TokenWriteProtected + * \li ArgumentsBad + * @return true on success, false otherwise + */ + bool C_OpenSession(SlotId slot_id, + Flags flags, + VoidPtr application, + Notify notify, + SessionHandle* session_ptr, + ReturnValue* return_value = ThrowException) const; + + /** + * C_CloseSession closes a session between an application and a token. + * @param session the session's handle + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory + * \li DeviceRemoved \li FunctionFailed \li GeneralError + * \li HostMemory \li OK \li SessionClosed + * \li SessionHandleInvalid + * @return true on success, false otherwise + */ + bool C_CloseSession(SessionHandle session, + ReturnValue* return_value = ThrowException) const; + + /** + * C_CloseAllSessions closes all sessions with a token. + * @param slot_id the token's slot + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory + * \li DeviceRemoved \li FunctionFailed \li GeneralError + * \li HostMemory \li OK \li SlotIdInvalid + * \li TokenNotPresent + * @return true on success, false otherwise + */ + bool C_CloseAllSessions(SlotId slot_id, + ReturnValue* return_value = ThrowException) const; + + /** + * C_GetSessionInfo obtains information about the session. + * @param session the session's handle + * @param info receives session info + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory + * \li DeviceRemoved \li FunctionFailed \li GeneralError + * \li HostMemory \li OK \li SessionClosed + * \li SessionHandleInvalid \li ArgumentsBad + * @return true on success, false otherwise + */ + bool C_GetSessionInfo(SessionHandle session, + SessionInfo* info_ptr, + ReturnValue* return_value = ThrowException) const; + + /** + * C_GetOperationState obtains the state of the cryptographic operation in a session. + * @param session session's handle + * @param operation_state_ptr gets state + * @param operation_state_len_ptr gets state length + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li BufferTooSmall \li CryptokiNotInitialized \li DeviceError + * \li DeviceMemory \li DeviceRemoved \li FunctionFailed + * \li GeneralError \li HostMemory \li OK + * \li OperationNotInitialized \li SessionClosed \li SessionHandleInvalid + * \li StateUnsaveable \li ArgumentsBad + * @return true on success, false otherwise + */ + bool C_GetOperationState(SessionHandle session, + Byte* operation_state_ptr, + Ulong* operation_state_len_ptr, + ReturnValue* return_value = ThrowException) const; + + /** + * C_SetOperationState restores the state of the cryptographic operation in a session. + * @param session session's handle + * @param operation_state_ptr holds state + * @param operation_state_len holds state length + * @param encryption_key en/decryption key + * @param authentication_key sign/verify key + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory + * \li DeviceRemoved \li FunctionFailed \li GeneralError + * \li HostMemory \li KeyChanged \li KeyNeeded + * \li KeyNotNeeded \li OK \li SavedStateInvalid + * \li SessionClosed \li SessionHandleInvalid \li ArgumentsBad + * @return true on success, false otherwise + */ + bool C_SetOperationState(SessionHandle session, + Byte* operation_state_ptr, + Ulong operation_state_len, + ObjectHandle encryption_key, + ObjectHandle authentication_key, + ReturnValue* return_value = ThrowException) const; + + /** + * C_Login logs a user into a token. + * @param session the session's handle + * @param user_type the user type + * @param pin_ptr the user's PIN + * @param pin_len the length of the PIN + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError + * \li DeviceMemory \li DeviceRemoved \li FunctionCanceled + * \li FunctionFailed \li GeneralError \li HostMemory + * \li OK \li OperationNotInitialized \li PinIncorrect + * \li PinLocked \li SessionClosed \li SessionHandleInvalid + * \li SessionReadOnlyExists \li UserAlreadyLoggedIn \li UserAnotherAlreadyLoggedIn + * \li UserPinNotInitialized \li UserTooManyTypes \li UserTypeInvalid + * @return true on success, false otherwise + */ + bool C_Login(SessionHandle session, + UserType user_type, + Utf8Char* pin_ptr, + Ulong pin_len, + ReturnValue* return_value = ThrowException) const; + + /** + * C_Login logs a user into a token. + * @param session the session's handle + * @param user_type the user type + * @param pin the user or security officer's PIN + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError + * \li DeviceMemory \li DeviceRemoved \li FunctionCanceled + * \li FunctionFailed \li GeneralError \li HostMemory + * \li OK \li OperationNotInitialized \li PinIncorrect + * \li PinLocked \li SessionClosed \li SessionHandleInvalid + * \li SessionReadOnlyExists \li UserAlreadyLoggedIn \li UserAnotherAlreadyLoggedIn + * \li UserPinNotInitialized \li UserTooManyTypes \li UserTypeInvalid + * @return true on success, false otherwise + */ + template + bool C_Login(SessionHandle session, + UserType user_type, + const std::vector& pin, + ReturnValue* return_value = ThrowException) const + { + return C_Login(session, user_type, reinterpret_cast< Utf8Char* >(const_cast< byte* >(pin.data())), pin.size(), + return_value); + } + + /** + * C_Logout logs a user out from a token. + * @param session the session's handle + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory + * \li DeviceRemoved \li FunctionFailed \li GeneralError + * \li HostMemory \li OK \li SessionClosed + * \li SessionHandleInvalid \li UserNotLoggedIn + * @return true on success, false otherwise + */ + bool C_Logout(SessionHandle session, + ReturnValue* return_value = ThrowException) const; + + /****************************** Object management functions ******************************/ + + /** + * C_CreateObject creates a new object. + * @param session the session's handle + * @param attribute_template_ptr the object's template + * @param count attributes in template + * @param object_ptr gets new object's handle. + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li AttributeReadOnly \li AttributeTypeInvalid + * \li AttributeValueInvalid \li CryptokiNotInitialized \li CurveNotSupported + * \li DeviceError \li DeviceMemory \li DeviceRemoved + * \li DomainParamsInvalid \li FunctionFailed \li GeneralError + * \li HostMemory \li OK \li PinExpired + * \li SessionClosed \li SessionHandleInvalid \li SessionReadOnly + * \li TemplateIncomplete \li TemplateInconsistent \li TokenWriteProtected + * \li UserNotLoggedIn + * @return true on success, false otherwise + */ + bool C_CreateObject(SessionHandle session, + Attribute* attribute_template_ptr, + Ulong count, + ObjectHandle* object_ptr, + ReturnValue* return_value = ThrowException) const; + + /** + * C_CopyObject copies an object, creating a new object for the copy. + * @param session the session's handle + * @param object the object's handle + * @param attribute_template_ptr template for new object + * @param count attributes in template + * @param new_object_ptr receives handle of copy + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ActionProhibited \li ArgumentsBad \li AttributeReadOnly + * \li AttributeTypeInvalid \li AttributeValueInvalid \li CryptokiNotInitialized + * \li DeviceError \li DeviceMemory \li DeviceRemoved + * \li FunctionFailed \li GeneralError \li HostMemory + * \li ObjectHandleInvalid \li OK \li PinExpired + * \li SessionClosed \li SessionHandleInvalid \li SessionReadOnly + * \li TemplateInconsistent \li TokenWriteProtected \li UserNotLoggedIn + * @return true on success, false otherwise + */ + bool C_CopyObject(SessionHandle session, + ObjectHandle object, + Attribute* attribute_template_ptr, + Ulong count, + ObjectHandle* new_object_ptr, + ReturnValue* return_value = ThrowException) const; + + /** + * C_DestroyObject destroys an object. + * @param session the session's handle + * @param object the object's handle + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ActionProhibited \li CryptokiNotInitialized \li DeviceError + * \li DeviceMemory \li DeviceRemoved \li FunctionFailed + * \li GeneralError \li HostMemory \li ObjectHandleInvalid + * \li OK \li PinExpired \li SessionClosed + * \li SessionHandleInvalid \li SessionReadOnly \li TokenWriteProtected + * @return true on success, false otherwise + */ + bool C_DestroyObject(SessionHandle session, + ObjectHandle object, + ReturnValue* return_value = ThrowException) const; + + /** + * C_GetObjectSize gets the size of an object in bytes. + * @param session the session's handle + * @param object the object's handle + * @param size_ptr receives size of object + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError + * \li DeviceMemory \li DeviceRemoved \li FunctionFailed + * \li GeneralError \li HostMemory \li InformationSensitive + * \li ObjectHandleInvalid \li OK \li SessionClosed + * \li SessionHandleInvalid + * @return true on success, false otherwise + */ + bool C_GetObjectSize(SessionHandle session, + ObjectHandle object, + Ulong* size_ptr, + ReturnValue* return_value = ThrowException) const; + + /** + * C_GetAttributeValue obtains the value of one or more object attributes. + * @param session the session's handle + * @param object the object's handle + * @param attribute_template_ptr specifies attrs; gets vals + * @param count attributes in template + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li AttributeSensitive \li AttributeTypeInvalid + * \li BufferTooSmall \li CryptokiNotInitialized \li DeviceError + * \li DeviceMemory \li DeviceRemoved \li FunctionFailed + * \li GeneralError \li HostMemory \li ObjectHandleInvalid + * \li OK \li SessionClosed \li SessionHandleInvalid + * @return true on success, false otherwise + */ + bool C_GetAttributeValue(SessionHandle session, + ObjectHandle object, + Attribute* attribute_template_ptr, + Ulong count, + ReturnValue* return_value = ThrowException) const; + + /** + * C_GetAttributeValue obtains the value of one or more object attributes. + * @param session the session's handle + * @param object the object's handle + * @param attribute_values specifies attrs; gets vals + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li AttributeSensitive \li AttributeTypeInvalid + * \li BufferTooSmall \li CryptokiNotInitialized \li DeviceError + * \li DeviceMemory \li DeviceRemoved \li FunctionFailed + * \li GeneralError \li HostMemory \li ObjectHandleInvalid + * \li OK \li SessionClosed \li SessionHandleInvalid + * @return true on success, false otherwise + */ + template + bool C_GetAttributeValue(SessionHandle session, + ObjectHandle object, + std::map>& attribute_values, + ReturnValue* return_value = ThrowException) const + { + std::vector getter_template; + + for(const auto& entry : attribute_values) + { + getter_template.emplace_back(Attribute{ static_cast< CK_ATTRIBUTE_TYPE >(entry.first), nullptr, 0 }); + } + + bool success = C_GetAttributeValue(session, object, const_cast< Attribute* >(getter_template.data()), + getter_template.size(), return_value); + + if(!success) + { + return success; + } + + size_t i = 0; + for(auto& entry : attribute_values) + { + entry.second.clear(); + entry.second.resize(getter_template.at(i).ulValueLen); + getter_template.at(i).pValue = const_cast< byte* >(entry.second.data()); + i++; + } + + return C_GetAttributeValue(session, object, const_cast< Attribute* >(getter_template.data()), getter_template.size(), + return_value); + } + + /** + * C_SetAttributeValue modifies the value of one or more object attributes. + * @param session the session's handle + * @param object the object's handle + * @param attribute_template_ptr specifies attrs and values + * @param count attributes in template + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ActionProhibited \li ArgumentsBad \li AttributeReadOnly + * \li AttributeTypeInvalid \li AttributeValueInvalid \li CryptokiNotInitialized + * \li DeviceError \li DeviceMemory \li DeviceRemoved + * \li FunctionFailed \li GeneralError \li HostMemory + * \li ObjectHandleInvalid \li OK \li SessionClosed + * \li SessionHandleInvalid \li SessionReadOnly \li TemplateInconsistent + * \li TokenWriteProtected \li UserNotLoggedIn + * @return true on success, false otherwise + */ + bool C_SetAttributeValue(SessionHandle session, + ObjectHandle object, + Attribute* attribute_template_ptr, + Ulong count, + ReturnValue* return_value = ThrowException) const; + + /** + * C_SetAttributeValue modifies the value of one or more object attributes. + * @param session the session's handle + * @param object the object's handle + * @param attributes specifies attrs and values + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ActionProhibited \li ArgumentsBad \li AttributeReadOnly + * \li AttributeTypeInvalid \li AttributeValueInvalid \li CryptokiNotInitialized + * \li DeviceError \li DeviceMemory \li DeviceRemoved + * \li FunctionFailed \li GeneralError \li HostMemory + * \li ObjectHandleInvalid \li OK \li SessionClosed + * \li SessionHandleInvalid \li SessionReadOnly \li TemplateInconsistent + * \li TokenWriteProtected \li UserNotLoggedIn + * @return true on success, false otherwise + */ + template + bool C_SetAttributeValue(SessionHandle session, + ObjectHandle object, + std::map>& attribute_values, + ReturnValue* return_value = ThrowException) const + { + std::vector setter_template; + + for(auto& entry : attribute_values) + { + setter_template.emplace_back(Attribute{ static_cast< CK_ATTRIBUTE_TYPE >(entry.first), entry.second.data(), static_cast(entry.second.size()) }); + } + + return C_SetAttributeValue(session, object, const_cast< Attribute* >(setter_template.data()), setter_template.size(), + return_value); + } + + /** + * C_FindObjectsInit initializes a search for token and session objects that match a template. + * @param session the session's handle + * @param attribute_template_ptr attribute values to match + * @param count attrs in search template + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li AttributeTypeInvalid \li AttributeValueInvalid + * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory + * \li DeviceRemoved \li FunctionFailed \li GeneralError + * \li HostMemory \li OK \li OperationActive + * \li PinExpired \li SessionClosed \li SessionHandleInvalid + * @return true on success, false otherwise + */ + bool C_FindObjectsInit(SessionHandle session, + Attribute* attribute_template_ptr, + Ulong count, + ReturnValue* return_value = ThrowException) const; + + /** + * C_FindObjects continues a search for token and session objects that match a template, obtaining additional object handles. + * @param session session's handle + * @param object gets obj. handles + * @param max_object_count max handles to get + * @param object_count actual # returned + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError + * \li DeviceMemory \li DeviceRemoved \li FunctionFailed + * \li GeneralError \li HostMemory \li OK + * \li OperationNotInitialized \li SessionClosed \li SessionHandleInvalid + * @return true on success, false otherwise + */ + bool C_FindObjects(SessionHandle session, + ObjectHandle* object_ptr, + Ulong max_object_count, + Ulong* object_count_ptr, + ReturnValue* return_value = ThrowException) const; + + /** + * C_FindObjectsFinal finishes a search for token and session objects. + * @param session the session's handle + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory + * \li DeviceRemoved \li FunctionFailed \li GeneralError + * \li HostMemory \li OK \li OperationNotInitialized + * \li SessionClosed \li SessionHandleInvalid + * @return true on success, false otherwise + */ + bool C_FindObjectsFinal(SessionHandle session, + ReturnValue* return_value = ThrowException) const; + + /****************************** Encryption functions ******************************/ + + /** + * C_EncryptInit initializes an encryption operation. + * @param session the session's handle + * @param mechanism_ptr the encryption mechanism + * @param key handle of encryption key + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory + * \li DeviceRemoved \li FunctionCanceled \li FunctionFailed + * \li GeneralError \li HostMemory \li KeyFunctionNotPermitted + * \li KeyHandleInvalid \li KeySizeRange \li KeyTypeInconsistent + * \li MechanismInvalid \li MechanismParamInvalid \li OK + * \li OperationActive \li PinExpired \li SessionClosed + * \li SessionHandleInvalid \li UserNotLoggedIn + * @return true on success, false otherwise + */ + bool C_EncryptInit(SessionHandle session, + Mechanism* mechanism_ptr, + ObjectHandle key, + ReturnValue* return_value = ThrowException) const; + + /** + * C_Encrypt encrypts single-part data. + * @param session session's handle + * @param data_ptr the plaintext data + * @param encrypted_data_len_ptr bytes of plaintext + * @param encrypted_data gets ciphertext + * @param encrypted_data_len gets c-text size + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized + * \li DataInvalid \li DataLenRange \li DeviceError + * \li DeviceMemory \li DeviceRemoved \li FunctionCanceled + * \li FunctionFailed \li GeneralError \li HostMemory + * \li OK \li OperationNotInitialized \li SessionClosed + * \li SessionHandleInvalid + * @return true on success, false otherwise + */ + bool C_Encrypt(SessionHandle session, + Byte* data_ptr, + Ulong data_len, + Byte* encrypted_data, + Ulong* encrypted_data_len_ptr, + ReturnValue* return_value = ThrowException) const; + + /** + * C_Encrypt encrypts single-part data. + * @param session session's handle + * @param plaintext_data the plaintext data + * @param encrypted_data gets ciphertext + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized + * \li DataInvalid \li DataLenRange \li DeviceError + * \li DeviceMemory \li DeviceRemoved \li FunctionCanceled + * \li FunctionFailed \li GeneralError \li HostMemory + * \li OK \li OperationNotInitialized \li SessionClosed + * \li SessionHandleInvalid + * @return true on success, false otherwise + */ + template + bool C_Encrypt(SessionHandle session, + const std::vector& plaintext_data, + std::vector& encrypted_data, + ReturnValue* return_value = ThrowException) const + { + Ulong encrypted_size = 0; + if(!C_Encrypt(session, const_cast((plaintext_data.data())), plaintext_data.size(), nullptr, &encrypted_size, + return_value)) + { + return false; + } + + encrypted_data.resize(encrypted_size); + return C_Encrypt(session, const_cast(plaintext_data.data()), plaintext_data.size(), encrypted_data.data(), + &encrypted_size, return_value); + } + + /** + * C_EncryptUpdate continues a multiple-part encryption operation. + * @param session session's handle + * @param part_ptr the plaintext data + * @param part_len plaintext data len + * @param encrypted_part_ptr gets ciphertext + * @param encrypted_part_len_ptr gets c-text size + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized + * \li DataLenRange \li DeviceError \li DeviceMemory + * \li DeviceRemoved \li FunctionCanceled \li FunctionFailed + * \li GeneralError \li HostMemory \li OK + * \li OperationNotInitialized \li SessionClosed \li SessionHandleInvalid + * @return true on success, false otherwise + */ + bool C_EncryptUpdate(SessionHandle session, + Byte* part_ptr, + Ulong part_len, + Byte* encrypted_part_ptr, + Ulong* encrypted_part_len_ptr, + ReturnValue* return_value = ThrowException) const; + + /** + * C_EncryptFinal finishes a multiple-part encryption operation. + * @param session session handle + * @param last_encrypted_part_ptr last c-text + * @param last_encrypted_part_len_ptr gets last size + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized + * \li DataLenRange \li DeviceError \li DeviceMemory + * \li DeviceRemoved \li FunctionCanceled \li FunctionFailed + * \li GeneralError \li HostMemory \li OK + * \li OperationNotInitialized \li SessionClosed \li SessionHandleInvalid + * @return true on success, false otherwise + */ + bool C_EncryptFinal(SessionHandle session, + Byte* last_encrypted_part_ptr, + Ulong* last_encrypted_part_len_ptr, + ReturnValue* return_value = ThrowException) const; + + /****************************** Decryption functions ******************************/ + + /** + * C_DecryptInit initializes a decryption operation. + * @param session the session's handle + * @param mechanism_ptr the decryption mechanism + * @param key handle of decryption key + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError + * \li DeviceMemory \li DeviceRemoved \li FunctionCanceled + * \li FunctionFailed \li GeneralError \li HostMemory + * \li KeyFunctionNotPermitted \li KeyHandleInvalid \li KeySizeRange + * \li KeyTypeInconsistent \li MechanismInvalid \li MechanismParamInvalid + * \li OK \li OperationActive \li PinExpired + * \li SessionClosed \li SessionHandleInvalid \li UserNotLoggedIn + * @return true on success, false otherwise + */ + bool C_DecryptInit(SessionHandle session, + Mechanism* mechanism_ptr, + ObjectHandle key, + ReturnValue* return_value = ThrowException) const; + + /** + * C_Decrypt decrypts encrypted data in a single part. + * @param session session's handle + * @param encrypted_data_ptr ciphertext + * @param encrypted_data_len ciphertext length + * @param data_ptr gets plaintext + * @param data_len_ptr gets p-text size + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized + * \li DeviceError \li DeviceMemory \li DeviceRemoved + * \li EncryptedDataInvalid \li EncryptedDataLenRange \li FunctionCanceled + * \li FunctionFailed \li GeneralError \li HostMemory + * \li OK \li OperationNotInitialized \li SessionClosed + * \li SessionHandleInvalid \li UserNotLoggedIn + * @return true on success, false otherwise + */ + bool C_Decrypt(SessionHandle session, + Byte* encrypted_data_ptr, + Ulong encrypted_data_len, + Byte* data_ptr, + Ulong* data_len_ptr, + ReturnValue* return_value = ThrowException) const; + + /** + * C_Decrypt decrypts encrypted data in a single part. + * @param session session's handle + * @param encrypted_data ciphertext + * @param decrypted_data gets plaintext + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized + * \li DeviceError \li DeviceMemory \li DeviceRemoved + * \li EncryptedDataInvalid \li EncryptedDataLenRange \li FunctionCanceled + * \li FunctionFailed \li GeneralError \li HostMemory + * \li OK \li OperationNotInitialized \li SessionClosed + * \li SessionHandleInvalid \li UserNotLoggedIn + * @return true on success, false otherwise + */ + template + bool C_Decrypt(SessionHandle session, + const std::vector& encrypted_data, + std::vector& decrypted_data, + ReturnValue* return_value = ThrowException) const + { + Ulong decrypted_size = 0; + if(!C_Decrypt(session, const_cast((encrypted_data.data())), encrypted_data.size(), nullptr, &decrypted_size, + return_value)) + { + return false; + } + + decrypted_data.resize(decrypted_size); + return C_Decrypt(session, const_cast(encrypted_data.data()), encrypted_data.size(), decrypted_data.data(), + &decrypted_size, return_value); + } + + /** + * C_DecryptUpdate continues a multiple-part decryption operation. + * @param session session's handle + * @param encrypted_part_ptr encrypted data + * @param encrypted_part_len input length + * @param part_ptr gets plaintext + * @param part_len_ptr p-text size + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized + * \li DeviceError \li DeviceMemory \li DeviceRemoved + * \li EncryptedDataInvalid \li EncryptedDataLenRange \li FunctionCanceled + * \li FunctionFailed \li GeneralError \li HostMemory + * \li OK \li OperationNotInitialized \li SessionClosed + * \li SessionHandleInvalid \li UserNotLoggedIn + * @return true on success, false otherwise + */ + bool C_DecryptUpdate(SessionHandle session, + Byte* encrypted_part_ptr, + Ulong encrypted_part_len, + Byte* part_ptr, + Ulong* part_len_ptr, + ReturnValue* return_value = ThrowException) const; + + /** + * C_DecryptFinal finishes a multiple-part decryption operation. + * @param session the session's handle + * @param last_part_ptr gets plaintext + * @param last_part_len_ptr p-text size + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized + * \li DeviceError \li DeviceMemory \li DeviceRemoved + * \li EncryptedDataInvalid \li EncryptedDataLenRange \li FunctionCanceled + * \li FunctionFailed \li GeneralError \li HostMemory + * \li OK \li OperationNotInitialized \li SessionClosed + * \li SessionHandleInvalid \li UserNotLoggedIn + * @return true on success, false otherwise + */ + bool C_DecryptFinal(SessionHandle session, + Byte* last_part_ptr, + Ulong* last_part_len_ptr, + ReturnValue* return_value = ThrowException) const; + + /****************************** Message digesting functions ******************************/ + + /** + * C_DigestInit initializes a message-digesting operation. + * @param session the session's handle + * @param mechanism_ptr the digesting mechanism + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError + * \li DeviceMemory \li DeviceRemoved \li FunctionCanceled + * \li FunctionFailed \li GeneralError \li HostMemory + * \li MechanismInvalid \li MechanismParamInvalid \li OK + * \li OperationActive \li PinExpired \li SessionClosed + * \li SessionHandleInvalid \li UserNotLoggedIn + * @return true on success, false otherwise + */ + bool C_DigestInit(SessionHandle session, + Mechanism* mechanism_ptr, + ReturnValue* return_value = ThrowException) const; + + /** + * C_Digest digests data in a single part. + * @param session the session's handle + * @param data_ptr data to be digested + * @param data_len bytes of data to digest + * @param digest_ptr gets the message digest + * @param digest_len_ptr gets digest length + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized + * \li DeviceError \li DeviceMemory \li DeviceRemoved + * \li FunctionCanceled \li FunctionFailed \li GeneralError + * \li HostMemory \li OK \li OperationNotInitialized + * \li SessionClosed \li SessionHandleInvalid + * @return true on success, false otherwise + */ + bool C_Digest(SessionHandle session, + Byte* data_ptr, + Ulong data_len, + Byte* digest_ptr, + Ulong* digest_len_ptr, + ReturnValue* return_value = ThrowException) const; + + /** + * C_DigestUpdate continues a multiple-part message-digesting operation. + * @param session the session's handle + * @param part_ptr data to be digested + * @param part_len bytes of data to be digested + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError + * \li DeviceMemory \li DeviceRemoved \li FunctionCanceled + * \li FunctionFailed \li GeneralError \li HostMemory + * \li OK \li OperationNotInitialized \li SessionClosed + * \li SessionHandleInvalid + * @return true on success, false otherwise + */ + bool C_DigestUpdate(SessionHandle session, + Byte* part_ptr, + Ulong part_len, + ReturnValue* return_value = ThrowException) const; + + /** + * C_DigestKey continues a multi-part message-digesting operation, by digesting the value of a secret key as part of the data already digested. + * @param session the session's handle + * @param key secret key to digest + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory + * \li DeviceRemoved \li FunctionCanceled \li FunctionFailed + * \li GeneralError \li HostMemory \li KeyHandleInvalid + * \li KeyIndigestible \li KeySizeRange \li OK + * \li OperationNotInitialized \li SessionClosed \li SessionHandleInvalid + * @return true on success, false otherwise + */ + bool C_DigestKey(SessionHandle session, + ObjectHandle key, + ReturnValue* return_value = ThrowException) const; + + /** + * C_DigestFinal finishes a multiple-part message-digesting operation. + * @param session the session's handle + * @param digest_ptr gets the message digest + * @param digest_len_ptr gets byte count of digest + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized + * \li DeviceError \li DeviceMemory \li DeviceRemoved + * \li FunctionCanceled \li FunctionFailed \li GeneralError + * \li HostMemory \li OK \li OperationNotInitialized + * \li SessionClosed \li SessionHandleInvalid + * @return true on success, false otherwise + */ + bool C_DigestFinal(SessionHandle session, + Byte* digest_ptr, + Ulong* digest_len_ptr, + ReturnValue* return_value = ThrowException) const; + + /****************************** Signing and MACing functions ******************************/ + + /** + * C_SignInit initializes a signature (private key encryption) operation, where the signature is (will be) an appendix to the data, and plaintext cannot be recovered from the signature. + * @param session the session's handle + * @param mechanism_ptr the signature mechanism + * @param key handle of signature key + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError + * \li DeviceMemory \li DeviceRemoved \li FunctionCanceled + * \li FunctionFailed \li GeneralError \li HostMemory + * \li KeyFunctionNotPermitted \li KeyHandleInvalid \li KeySizeRange + * \li KeyTypeInconsistent \li MechanismInvalid \li MechanismParamInvalid + * \li OK \li OperationActive \li PinExpired + * \li SessionClosed \li SessionHandleInvalid \li UserNotLoggedIn + * @return true on success, false otherwise + */ + bool C_SignInit(SessionHandle session, + Mechanism* mechanism_ptr, + ObjectHandle key, + ReturnValue* return_value = ThrowException) const; + + /** + * C_Sign signs (encrypts with private key) data in a single part, where the signature is (will be) an appendix to the data, and plaintext cannot be recovered from the signature. + * @param session the session's handle + * @param data_ptr the data to sign + * @param data_len count of bytes to sign + * @param signature_ptr gets the signature + * @param signature_len_ptr gets signature length + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized + * \li DataInvalid \li DataLenRange \li DeviceError + * \li DeviceMemory \li DeviceRemoved \li FunctionCanceled + * \li FunctionFailed \li GeneralError \li HostMemory + * \li OK \li OperationNotInitialized \li SessionClosed + * \li SessionHandleInvalid \li UserNotLoggedIn \li FunctionRejected + * @return true on success, false otherwise + */ + bool C_Sign(SessionHandle session, + Byte* data_ptr, + Ulong data_len, + Byte* signature_ptr, + Ulong* signature_len_ptr, + ReturnValue* return_value = ThrowException) const; + + /** + * C_Sign signs (encrypts with private key) data in a single part, where the signature is (will be) an appendix to the data, and plaintext cannot be recovered from the signature. + * @param session the session's handle + * @param data the data to sign + * @param signature gets the signature + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized + * \li DataInvalid \li DataLenRange \li DeviceError + * \li DeviceMemory \li DeviceRemoved \li FunctionCanceled + * \li FunctionFailed \li GeneralError \li HostMemory + * \li OK \li OperationNotInitialized \li SessionClosed + * \li SessionHandleInvalid \li UserNotLoggedIn \li FunctionRejected + * @return true on success, false otherwise + */ + template + bool C_Sign(SessionHandle session, + const std::vector& data, + std::vector& signature, + ReturnValue* return_value = ThrowException) const + { + Ulong signature_size = 0; + if(!C_Sign(session, const_cast((data.data())), data.size(), nullptr, &signature_size, return_value)) + { + return false; + } + + signature.resize(signature_size); + return C_Sign(session, const_cast(data.data()), data.size(), signature.data(), &signature_size, return_value); + } + + /** + * C_SignUpdate continues a multiple-part signature operation, where the signature is (will be) an appendix to the data, and plaintext cannot be recovered from the signature. + * @param session the session's handle + * @param part_ptr the data to sign + * @param part_len count of bytes to sign + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li CryptokiNotInitialized \li DataLenRange + * \li DeviceError \li DeviceMemory \li DeviceRemoved + * \li FunctionCanceled \li FunctionFailed \li GeneralError + * \li HostMemory \li OK \li OperationNotInitialized + * \li SessionClosed \li SessionHandleInvalid \li UserNotLoggedIn + * @return true on success, false otherwise + */ + bool C_SignUpdate(SessionHandle session, + Byte* part_ptr, + Ulong part_len, + ReturnValue* return_value = ThrowException) const; + + /** + * C_SignUpdate continues a multiple-part signature operation, where the signature is (will be) an appendix to the data, and plaintext cannot be recovered from the signature. + * @param session the session's handle + * @param part the data to sign + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li CryptokiNotInitialized \li DataLenRange + * \li DeviceError \li DeviceMemory \li DeviceRemoved + * \li FunctionCanceled \li FunctionFailed \li GeneralError + * \li HostMemory \li OK \li OperationNotInitialized + * \li SessionClosed \li SessionHandleInvalid \li UserNotLoggedIn + * @return true on success, false otherwise + */ + template + bool C_SignUpdate(SessionHandle session, + const std::vector& part, + ReturnValue* return_value = ThrowException) const + { + return C_SignUpdate(session, const_cast(part.data()), part.size(), return_value); + } + + /** + * C_SignFinal finishes a multiple-part signature operation, returning the signature. + * @param session the session's handle + * @param signature_ptr gets the signature + * @param signature_len_ptr gets signature length + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized + * \li DataLenRange \li DeviceError \li DeviceMemory + * \li DeviceRemoved \li FunctionCanceled \li FunctionFailed + * \li GeneralError \li HostMemory \li OK + * \li OperationNotInitialized \li SessionClosed \li SessionHandleInvalid + * \li UserNotLoggedIn \li FunctionRejected + * @return true on success, false otherwise + */ + bool C_SignFinal(SessionHandle session, + Byte* signature_ptr, + Ulong* signature_len_ptr, + ReturnValue* return_value = ThrowException) const; + + /** + * C_SignFinal finishes a multiple-part signature operation, returning the signature. + * @param session the session's handle + * @param signature gets the signature + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized + * \li DataLenRange \li DeviceError \li DeviceMemory + * \li DeviceRemoved \li FunctionCanceled \li FunctionFailed + * \li GeneralError \li HostMemory \li OK + * \li OperationNotInitialized \li SessionClosed \li SessionHandleInvalid + * \li UserNotLoggedIn \li FunctionRejected + * @return true on success, false otherwise + */ + template + bool C_SignFinal(SessionHandle session, + std::vector& signature, + ReturnValue* return_value = ThrowException) const + { + Ulong signature_size = 0; + if(!C_SignFinal(session, nullptr, &signature_size, return_value)) + { + return false; + } + + signature.resize(signature_size); + return C_SignFinal(session, signature.data(), &signature_size, return_value); + } + + /** + * C_SignRecoverInit initializes a signature operation, where the data can be recovered from the signature. + * @param session the session's handle + * @param mechanism_ptr the signature mechanism + * @param key handle of the signature key + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError + * \li DeviceMemory \li DeviceRemoved \li FunctionCanceled + * \li FunctionFailed \li GeneralError \li HostMemory + * \li KeyFunctionNotPermitted \li KeyHandleInvalid \li KeySizeRange + * \li KeyTypeInconsistent \li MechanismInvalid \li MechanismParamInvalid + * \li OK \li OperationActive \li PinExpired + * \li SessionClosed \li SessionHandleInvalid \li UserNotLoggedIn + * @return true on success, false otherwise + */ + bool C_SignRecoverInit(SessionHandle session, + Mechanism* mechanism_ptr, + ObjectHandle key, + ReturnValue* return_value = ThrowException) const; + + /** + * C_SignRecover signs data in a single operation, where the data can be recovered from the signature. + * @param session the session's handle + * @param data_ptr the data to sign + * @param data_len count of bytes to sign + * @param signature_ptr gets the signature + * @param signature_len_ptr gets signature length + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized + * \li DataInvalid \li DataLenRange \li DeviceError + * \li DeviceMemory \li DeviceRemoved \li FunctionCanceled + * \li FunctionFailed \li GeneralError \li HostMemory + * \li OK \li OperationNotInitialized \li SessionClosed + * \li SessionHandleInvalid \li UserNotLoggedIn + * @return true on success, false otherwise + */ + bool C_SignRecover(SessionHandle session, + Byte* data_ptr, + Ulong data_len, + Byte* signature_ptr, + Ulong* signature_len_ptr, + ReturnValue* return_value = ThrowException) const; + + /****************************** Functions for verifying signatures and MACs ******************************/ + + /** + * C_VerifyInit initializes a verification operation, where the signature is an appendix to the data, and plaintext cannot be recovered from the signature (e.g. DSA). + * @param session the session's handle + * @param mechanism_ptr the verification mechanism + * @param key verification key + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError + * \li DeviceMemory \li DeviceRemoved \li FunctionCanceled + * \li FunctionFailed \li GeneralError \li HostMemory + * \li KeyFunctionNotPermitted \li KeyHandleInvalid \li KeySizeRange + * \li KeyTypeInconsistent \li MechanismInvalid \li MechanismParamInvalid + * \li OK \li OperationActive \li PinExpired + * \li SessionClosed \li SessionHandleInvalid \li UserNotLoggedIn + * @return true on success, false otherwise + */ + bool C_VerifyInit(SessionHandle session, + Mechanism* mechanism_ptr, + ObjectHandle key, + ReturnValue* return_value = ThrowException) const; + + /** + * C_Verify verifies a signature in a single-part operation, where the signature is an appendix to the data, and plaintext cannot be recovered from the signature. + * @param session the session's handle + * @param data_ptr signed data + * @param data_len length of signed data + * @param signature_ptr signature + * @param signature_len signature length + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li CryptokiNotInitialized \li DataInvalid + * \li DataLenRange \li DeviceError \li DeviceMemory + * \li DeviceRemoved \li FunctionCanceled \li FunctionFailed + * \li GeneralError \li HostMemory \li OK + * \li OperationNotInitialized \li SessionClosed \li SessionHandleInvalid + * \li SignatureInvalid \li SignatureLenRange + * @return true on success, false otherwise + */ + bool C_Verify(SessionHandle session, + Byte* data_ptr, + Ulong data_len, + Byte* signature_ptr, + Ulong signature_len, + ReturnValue* return_value = ThrowException) const; + + /** + * C_Verify verifies a signature in a single-part operation, where the signature is an appendix to the data, and plaintext cannot be recovered from the signature. + * @param session the session's handle + * @param data signed data + * @param signature signature + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li CryptokiNotInitialized \li DataInvalid + * \li DataLenRange \li DeviceError \li DeviceMemory + * \li DeviceRemoved \li FunctionCanceled \li FunctionFailed + * \li GeneralError \li HostMemory \li OK + * \li OperationNotInitialized \li SessionClosed \li SessionHandleInvalid + * \li SignatureInvalid \li SignatureLenRange + * @return true on success, false otherwise + */ + template + bool C_Verify(SessionHandle session, + const std::vector& data, + std::vector& signature, + ReturnValue* return_value = ThrowException) const + { + return C_Verify(session, const_cast(data.data()), data.size(), signature.data(), signature.size(), return_value); + } + + /** + * C_VerifyUpdate continues a multiple-part verification operation, where the signature is an appendix to the data, and plaintext cannot be recovered from the signature. + * @param session the session's handle + * @param part_ptr signed data + * @param part_len length of signed data + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li CryptokiNotInitialized \li DataLenRange + * \li DeviceError \li DeviceMemory \li DeviceRemoved + * \li FunctionCanceled \li FunctionFailed \li GeneralError + * \li HostMemory \li OK \li OperationNotInitialized + * \li SessionClosed \li SessionHandleInvalid + * @return true on success, false otherwise + */ + bool C_VerifyUpdate(SessionHandle session, + Byte* part_ptr, + Ulong part_len, + ReturnValue* return_value = ThrowException) const; + + /** + * C_VerifyUpdate continues a multiple-part verification operation, where the signature is an appendix to the data, and plaintext cannot be recovered from the signature. + * @param session the session's handle + * @param part signed data + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li CryptokiNotInitialized \li DataLenRange + * \li DeviceError \li DeviceMemory \li DeviceRemoved + * \li FunctionCanceled \li FunctionFailed \li GeneralError + * \li HostMemory \li OK \li OperationNotInitialized + * \li SessionClosed \li SessionHandleInvalid + * @return true on success, false otherwise + */ + template + bool C_VerifyUpdate(SessionHandle session, + std::vector part, + ReturnValue* return_value = ThrowException) const + { + return C_VerifyUpdate(session, part.data(), part.size(), return_value); + } + + /** + * C_VerifyFinal finishes a multiple-part verification operation, checking the signature. + * @param session the session's handle + * @param signature_ptr signature to verify + * @param signature_len signature length + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li CryptokiNotInitialized \li DataLenRange + * \li DeviceError \li DeviceMemory \li DeviceRemoved + * \li FunctionCanceled \li FunctionFailed \li GeneralError + * \li HostMemory \li OK \li OperationNotInitialized + * \li SessionClosed \li SessionHandleInvalid \li SignatureInvalid + * \li SignatureLenRange + * @return true on success, false otherwise + */ + bool C_VerifyFinal(SessionHandle session, + Byte* signature_ptr, + Ulong signature_len, + ReturnValue* return_value = ThrowException) const; + + /** + * C_VerifyRecoverInit initializes a signature verification operation, where the data is recovered from the signature. + * @param session the session's handle + * @param mechanism_ptr the verification mechanism + * @param key verification key + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError + * \li DeviceMemory \li DeviceRemoved \li FunctionCanceled + * \li FunctionFailed \li GeneralError \li HostMemory + * \li KeyFunctionNotPermitted \li KeyHandleInvalid \li KeySizeRange + * \li KeyTypeInconsistent \li MechanismInvalid \li MechanismParamInvalid + * \li OK \li OperationActive \li PinExpired + * \li SessionClosed \li SessionHandleInvalid \li UserNotLoggedIn + * @return true on success, false otherwise + */ + bool C_VerifyRecoverInit(SessionHandle session, + Mechanism* mechanism_ptr, + ObjectHandle key, + ReturnValue* return_value = ThrowException) const; + + /** + * C_VerifyRecover verifies a signature in a single-part operation, where the data is recovered from the signature. + * @param session the session's handle + * @param signature_ptr signature to verify + * @param signature_len signature length + * @param data_ptr gets signed data + * @param data_len_ptr gets signed data len + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized + * \li DataInvalid \li DataLenRange \li DeviceError + * \li DeviceMemory \li DeviceRemoved \li FunctionCanceled + * \li FunctionFailed \li GeneralError \li HostMemory + * \li OK \li OperationNotInitialized \li SessionClosed + * \li SessionHandleInvalid \li SignatureLenRange \li SignatureInvalid + * @return true on success, false otherwise + */ + bool C_VerifyRecover(SessionHandle session, + Byte* signature_ptr, + Ulong signature_len, + Byte* data_ptr, + Ulong* data_len_ptr, + ReturnValue* return_value = ThrowException) const; + + /****************************** Dual-purpose cryptographic functions ******************************/ + + /** + * C_DigestEncryptUpdate continues a multiple-part digesting and encryption operation. + * @param session session's handle + * @param part_ptr the plaintext data + * @param part_len plaintext length + * @param encrypted_part_ptr gets ciphertext + * @param encrypted_part_len_ptr gets c-text length + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized + * \li DataLenRange \li DeviceError \li DeviceMemory + * \li DeviceRemoved \li FunctionCanceled \li FunctionFailed + * \li GeneralError \li HostMemory \li OK + * \li OperationNotInitialized \li SessionClosed \li SessionHandleInvalid + * @return true on success, false otherwise + */ + bool C_DigestEncryptUpdate(SessionHandle session, + Byte* part_ptr, + Ulong part_len, + Byte* encrypted_part_ptr, + Ulong* encrypted_part_len_ptr, + ReturnValue* return_value = ThrowException) const ; + + /** + * C_DecryptDigestUpdate continues a multiple-part decryption and digesting operation. + * @param session session's handle + * @param encrypted_part_ptr ciphertext + * @param encrypted_part_len ciphertext length + * @param part_ptr gets plaintext + * @param part_len_ptr gets plaintext len + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized + * \li DeviceError \li DeviceMemory \li DeviceRemoved + * \li EncryptedDataInvalid \li EncryptedDataLenRange \li FunctionCanceled + * \li FunctionFailed \li GeneralError \li HostMemory + * \li OK \li OperationNotInitialized \li SessionClosed + * \li SessionHandleInvalid + * @return true on success, false otherwise + */ + bool C_DecryptDigestUpdate(SessionHandle session, + Byte* encrypted_part_ptr, + Ulong encrypted_part_len, + Byte* part_ptr, + Ulong* part_len_ptr, + ReturnValue* return_value = ThrowException) const; + + /** + * C_SignEncryptUpdate continues a multiple-part signing and encryption operation. + * @param session session's handle + * @param part_ptr the plaintext data + * @param part_len plaintext length + * @param encrypted_part_ptr gets ciphertext + * @param encrypted_part_len_ptr gets c-text length + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized + * \li DataLenRange \li DeviceError \li DeviceMemory + * \li DeviceRemoved \li FunctionCanceled \li FunctionFailed + * \li GeneralError \li HostMemory \li OK + * \li OperationNotInitialized \li SessionClosed \li SessionHandleInvalid + * \li UserNotLoggedIn + * @return true on success, false otherwise + */ + bool C_SignEncryptUpdate(SessionHandle session, + Byte* part_ptr, + Ulong part_len, + Byte* encrypted_part_ptr, + Ulong* encrypted_part_len_ptr, + ReturnValue* return_value = ThrowException) const; + + /** + * C_DecryptVerifyUpdate continues a multiple-part decryption and verify operation. + * @param session session's handle + * @param encrypted_part_ptr ciphertext + * @param encrypted_part_len ciphertext length + * @param part_ptr gets plaintext + * @param part_len_ptr gets p-text length + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized + * \li DataLenRange \li DeviceError \li DeviceMemory + * \li DeviceRemoved \li EncryptedDataInvalid \li EncryptedDataLenRange + * \li FunctionCanceled \li FunctionFailed \li GeneralError + * \li HostMemory \li OK \li OperationNotInitialized + * \li SessionClosed \li SessionHandleInvalid + * @return true on success, false otherwise + */ + bool C_DecryptVerifyUpdate(SessionHandle session, + Byte* encrypted_part_ptr, + Ulong encrypted_part_len, + Byte* part_ptr, + Ulong* part_len_ptr, + ReturnValue* return_value = ThrowException) const; + + /****************************** Key management functions ******************************/ + + /** + * C_GenerateKey generates a secret key, creating a new key object. + * @param session the session's handle + * @param mechanism_ptr key generation mech. + * @param attribute_template_ptr template for new key + * @param count # of attrs in template + * @param key_ptr gets handle of new key + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li AttributeReadOnly \li AttributeTypeInvalid + * \li AttributeValueInvalid \li CryptokiNotInitialized \li CurveNotSupported + * \li DeviceError \li DeviceMemory \li DeviceRemoved + * \li FunctionCanceled \li FunctionFailed \li GeneralError + * \li HostMemory \li MechanismInvalid \li MechanismParamInvalid + * \li OK \li OperationActive \li PinExpired + * \li SessionClosed \li SessionHandleInvalid \li SessionReadOnly + * \li TemplateIncomplete \li TemplateInconsistent \li TokenWriteProtected + * \li UserNotLoggedIn + * @return true on success, false otherwise + */ + bool C_GenerateKey(SessionHandle session, + Mechanism* mechanism_ptr, + Attribute* attribute_template_ptr, + Ulong count, + ObjectHandle* key_ptr, + ReturnValue* return_value = ThrowException) const; + + /** + * C_GenerateKeyPair generates a public-key/private-key pair, creating new key objects. + * @param session session handle + * @param mechanism_ptr key-gen mech. + * @param public_key_template_ptr template for pub. key + * @param public_key_attribute_count # pub. attrs. + * @param private_key_template_ptr template for priv. key + * @param private_key_attribute_count # priv. attrs. + * @param public_key_ptr gets pub. key handle + * @param private_key_ptr gets priv. key handle + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li AttributeReadOnly \li AttributeTypeInvalid + * \li AttributeValueInvalid \li CryptokiNotInitialized \li CurveNotSupported + * \li DeviceError \li DeviceMemory \li DeviceRemoved + * \li DomainParamsInvalid \li FunctionCanceled \li FunctionFailed + * \li GeneralError \li HostMemory \li MechanismInvalid + * \li MechanismParamInvalid \li OK \li OperationActive + * \li PinExpired \li SessionClosed \li SessionHandleInvalid + * \li SessionReadOnly \li TemplateIncomplete \li TemplateInconsistent + * \li TokenWriteProtected \li UserNotLoggedIn + * @return true on success, false otherwise + */ + bool C_GenerateKeyPair(SessionHandle session, + Mechanism* mechanism_ptr, + Attribute* public_key_template_ptr, + Ulong public_key_attribute_count, + Attribute* private_key_template_ptr, + Ulong private_key_attribute_count, + ObjectHandle* public_key_ptr, + ObjectHandle* private_key_ptr, + ReturnValue* return_value = ThrowException) const; + + /** + * C_WrapKey wraps (i.e., encrypts) a key. + * @param session the session's handle + * @param mechanism_ptr the wrapping mechanism + * @param wrapping_key wrapping key + * @param key key to be wrapped + * @param wrapped_key_ptr gets wrapped key + * @param wrapped_key_len_ptr gets wrapped key size + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized + * \li DeviceError \li DeviceMemory \li DeviceRemoved + * \li FunctionCanceled \li FunctionFailed \li GeneralError + * \li HostMemory \li KeyHandleInvalid \li KeyNotWrappable + * \li KeySizeRange \li KeyUnextractable \li MechanismInvalid + * \li MechanismParamInvalid \li OK \li OperationActive + * \li PinExpired \li SessionClosed \li SessionHandleInvalid + * \li UserNotLoggedIn \li WrappingKeyHandleInvalid \li WrappingKeySizeRange + * \li WrappingKeyTypeInconsistent + * @return true on success, false otherwise + */ + bool C_WrapKey(SessionHandle session, + Mechanism* mechanism_ptr, + ObjectHandle wrapping_key, + ObjectHandle key, + Byte* wrapped_key_ptr, + Ulong* wrapped_key_len_ptr, + ReturnValue* return_value = ThrowException) const; + + /** + * C_UnwrapKey unwraps (decrypts) a wrapped key, creating a new key object. + * @param session session's handle + * @param mechanism_ptr unwrapping mech. + * @param unwrapping_key unwrapping key + * @param wrapped_key_ptr the wrapped key + * @param wrapped_key_len wrapped key len + * @param attribute_template_ptr new key template + * @param attribute_count template length + * @param key_ptr gets new handle + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li AttributeReadOnly \li AttributeTypeInvalid + * \li AttributeValueInvalid \li BufferTooSmall \li CryptokiNotInitialized + * \li CurveNotSupported \li DeviceError \li DeviceMemory + * \li DeviceRemoved \li DomainParamsInvalid \li FunctionCanceled + * \li FunctionFailed \li GeneralError \li HostMemory + * \li MechanismInvalid \li MechanismParamInvalid \li OK + * \li OperationActive \li PinExpired \li SessionClosed + * \li SessionHandleInvalid \li SessionReadOnly \li TemplateIncomplete + * \li TemplateInconsistent \li TokenWriteProtected \li UnwrappingKeyHandleInvalid + * \li UnwrappingKeySizeRange \li UnwrappingKeyTypeInconsistent \li UserNotLoggedIn + * \li WrappedKeyInvalid \li WrappedKeyLenRange + * @return true on success, false otherwise + */ + bool C_UnwrapKey(SessionHandle session, + Mechanism* mechanism_ptr, + ObjectHandle unwrapping_key, + Byte* wrapped_key_ptr, + Ulong wrapped_key_len, + Attribute* attribute_template_ptr, + Ulong attribute_count, + ObjectHandle* key_ptr, + ReturnValue* return_value = ThrowException) const; + + /** + * C_DeriveKey derives a key from a base key, creating a new key object. + * @param session session's handle + * @param mechanism_ptr key deriv. mech. + * @param base_key base key + * @param attribute_template_ptr new key template + * @param attribute_count template length + * @param key_ptr gets new handle + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li AttributeReadOnly \li AttributeTypeInvalid + * \li AttributeValueInvalid \li CryptokiNotInitialized \li CurveNotSupported + * \li DeviceError \li DeviceMemory \li DeviceRemoved + * \li DomainParamsInvalid \li FunctionCanceled \li FunctionFailed + * \li GeneralError \li HostMemory \li KeyHandleInvalid + * \li KeySizeRange \li KeyTypeInconsistent \li MechanismInvalid + * \li MechanismParamInvalid \li OK \li OperationActive + * \li PinExpired \li SessionClosed \li SessionHandleInvalid + * \li SessionReadOnly \li TemplateIncomplete \li TemplateInconsistent + * \li TokenWriteProtected \li UserNotLoggedIn + * @return true on success, false otherwise + */ + bool C_DeriveKey(SessionHandle session, + Mechanism* mechanism_ptr, + ObjectHandle base_key, + Attribute* attribute_template_ptr, + Ulong attribute_count, + ObjectHandle* key_ptr, + ReturnValue* return_value = ThrowException) const; + + /****************************** Random number generation functions ******************************/ + + /** + * C_SeedRandom mixes additional seed material into the token's random number generator. + * @param session the session's handle + * @param seed_ptr the seed material + * @param seed_len length of seed material + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError + * \li DeviceMemory \li DeviceRemoved \li FunctionCanceled + * \li FunctionFailed \li GeneralError \li HostMemory + * \li OK \li OperationActive \li RandomSeedNotSupported + * \li RandomNoRng \li SessionClosed \li SessionHandleInvalid + * \li UserNotLoggedIn + * @return true on success, false otherwise + */ + bool C_SeedRandom(SessionHandle session, + Byte* seed_ptr, + Ulong seed_len, + ReturnValue* return_value = ThrowException) const; + + /** + * C_GenerateRandom generates random data. + * @param session the session's handle + * @param random_data_ptr receives the random data + * @param random_len # of bytes to generate + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError + * \li DeviceMemory \li DeviceRemoved \li FunctionCanceled + * \li FunctionFailed \li GeneralError \li HostMemory + * \li OK \li OperationActive \li RandomNoRng + * \li SessionClosed \li SessionHandleInvalid \li UserNotLoggedIn + * @return true on success, false otherwise + */ + bool C_GenerateRandom(SessionHandle session, + Byte* random_data_ptr, + Ulong random_len, + ReturnValue* return_value = ThrowException) const; + + /****************************** Parallel function management functions ******************************/ + + /** + * C_GetFunctionStatus is a legacy function; it obtains an updated status of a function running in parallel with an application. + * @param session the session's handle + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li CryptokiNotInitialized \li FunctionFailed \li FunctionNotParallel + * \li GeneralError \li HostMemory \li SessionHandleInvalid + * \li SessionClosed + * @return true on success, false otherwise + */ + bool C_GetFunctionStatus(SessionHandle session, + ReturnValue* return_value = ThrowException) const; + + /** + * C_CancelFunction is a legacy function; it cancels a function running in parallel. + * @param session the session's handle + * @param return_value default value (`ThrowException`): throw exception on error. + * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown. + * At least the following PKCS#11 return values may be returned: + * \li CryptokiNotInitialized \li FunctionFailed \li FunctionNotParallel + * \li GeneralError \li HostMemory \li SessionHandleInvalid + * \li SessionClosed + * @return true on success, false otherwise + */ + bool C_CancelFunction(SessionHandle session, + ReturnValue* return_value = ThrowException) const; + + private: + const FunctionListPtr m_func_list_ptr; + }; + +class PKCS11_Error : public Exception + { + public: + explicit PKCS11_Error(const std::string& what) : + Exception("PKCS11 error", what) + { + } + }; + +class PKCS11_ReturnError : public PKCS11_Error + { + public: + explicit PKCS11_ReturnError(ReturnValue return_val) : + PKCS11_Error(std::to_string(static_cast< uint32_t >(return_val))), + m_return_val(return_val) + {} + + inline ReturnValue get_return_value() const + { + return m_return_val; + } + + private: + const ReturnValue m_return_val; + }; + +} + +} + +#endif diff --git a/src/lib/prov/pkcs11/p11_ecc_key.cpp b/src/lib/prov/pkcs11/p11_ecc_key.cpp new file mode 100644 index 000000000..4382b8c2b --- /dev/null +++ b/src/lib/prov/pkcs11/p11_ecc_key.cpp @@ -0,0 +1,137 @@ +/* +* PKCS#11 ECC +* (C) 2016 Daniel Neus, Sirrix AG +* (C) 2016 Philipp Weber, Sirrix AG +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO) + +#include +#include + +namespace Botan { +namespace PKCS11 { +namespace { +/// Converts a DER-encoded ANSI X9.62 ECPoint to PointGFp +PointGFp decode_public_point(const secure_vector& ec_point_data, const CurveGFp& curve) + { + secure_vector ec_point; + BER_Decoder(ec_point_data).decode(ec_point, OCTET_STRING); + return OS2ECP(ec_point, curve); + } +} + +EC_PublicKeyGenerationProperties::EC_PublicKeyGenerationProperties(const std::vector& ec_params) + : PublicKeyProperties(KeyType::Ec), m_ec_params(ec_params) + { + add_binary(AttributeType::EcParams, m_ec_params); + } + +EC_PublicKeyImportProperties::EC_PublicKeyImportProperties(const std::vector& ec_params, + const std::vector& ec_point) + : PublicKeyProperties(KeyType::Ec), m_ec_params(ec_params), m_ec_point(ec_point) + { + add_binary(AttributeType::EcParams, m_ec_params); + add_binary(AttributeType::EcPoint, m_ec_point); + } + +PKCS11_EC_PublicKey::PKCS11_EC_PublicKey(Session& session, ObjectHandle handle) + : EC_PublicKey(), Object(session, handle) + { + secure_vector ec_parameters = get_attribute_value(AttributeType::EcParams); + m_domain_params = EC_Group(unlock(ec_parameters)); + m_public_key = decode_public_point(get_attribute_value(AttributeType::EcPoint), m_domain_params.get_curve()); + m_domain_encoding = EC_DOMPAR_ENC_EXPLICIT; + } + +size_t PKCS11_EC_PublicKey::max_input_bits() const + { + return domain().get_order().bits(); + } + +PKCS11_EC_PublicKey::PKCS11_EC_PublicKey(Session& session, const EC_PublicKeyImportProperties& props) + : Object(session, props) + { + m_domain_params = EC_Group(props.ec_params()); + + secure_vector ec_point; + BER_Decoder(props.ec_point()).decode(ec_point, OCTET_STRING); + m_public_key = OS2ECP(ec_point, m_domain_params.get_curve()); + m_domain_encoding = EC_DOMPAR_ENC_EXPLICIT; + } + +EC_PrivateKeyImportProperties::EC_PrivateKeyImportProperties(const std::vector& ec_params, const BigInt& value) + : PrivateKeyProperties(KeyType::Ec), m_ec_params(ec_params), m_value(value) + { + add_binary(AttributeType::EcParams, m_ec_params); + add_binary(AttributeType::Value, BigInt::encode(m_value)); + } + +PKCS11_EC_PrivateKey::PKCS11_EC_PrivateKey(Session& session, ObjectHandle handle) + : Object(session, handle), m_domain_params(), m_public_key(), m_point_encoding(PublicPointEncoding::Der) + { + secure_vector ec_parameters = get_attribute_value(AttributeType::EcParams); + m_domain_params = EC_Group(unlock(ec_parameters)); + } + +PKCS11_EC_PrivateKey::PKCS11_EC_PrivateKey(Session& session, const EC_PrivateKeyImportProperties& props) + : Object(session, props) + { + m_domain_params = EC_Group(props.ec_params()); + } + +PKCS11_EC_PrivateKey::PKCS11_EC_PrivateKey(Session& session, const std::vector& ec_params, + const EC_PrivateKeyGenerationProperties& props) + : Object(session) + { + m_domain_params = EC_Group(ec_params); + + EC_PublicKeyGenerationProperties pub_key_props(ec_params); + pub_key_props.set_verify(true); + pub_key_props.set_private(false); + pub_key_props.set_token(false); // don't create a persistent public key object + + ObjectHandle pub_key_handle = 0; + m_handle = 0; + Mechanism mechanism = { CKM_EC_KEY_PAIR_GEN, nullptr, 0 }; + session.module()->C_GenerateKeyPair(session.handle(), &mechanism, + pub_key_props.data(), pub_key_props.count(), props.data(), props.count(), + &pub_key_handle, &m_handle); + + Object public_key(session, pub_key_handle); + m_public_key = decode_public_point(public_key.get_attribute_value(AttributeType::EcPoint), m_domain_params.get_curve()); + } + +size_t PKCS11_EC_PrivateKey::max_input_bits() const + { + return m_domain_params.get_order().bits(); + } + +std::vector PKCS11_EC_PrivateKey::x509_subject_public_key() const + { + return unlock(EC2OSP(public_point(), PointGFp::COMPRESSED)); + } + +size_t PKCS11_EC_PrivateKey::estimated_strength() const + { + return ecp_work_factor(domain().get_curve().get_p().bits()); + } + +bool PKCS11_EC_PrivateKey::check_key(RandomNumberGenerator&, bool) const + { + return m_public_key.on_the_curve(); + } + +AlgorithmIdentifier PKCS11_EC_PrivateKey::algorithm_identifier() const + { + return AlgorithmIdentifier(get_oid(), domain().DER_encode(EC_DOMPAR_ENC_EXPLICIT)); + } +} + +} + +#endif diff --git a/src/lib/prov/pkcs11/p11_ecc_key.h b/src/lib/prov/pkcs11/p11_ecc_key.h new file mode 100644 index 000000000..a32a78339 --- /dev/null +++ b/src/lib/prov/pkcs11/p11_ecc_key.h @@ -0,0 +1,229 @@ +/* +* PKCS#11 ECC +* (C) 2016 Daniel Neus, Sirrix AG +* (C) 2016 Philipp Weber, Sirrix AG +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_P11_ECC_H__ +#define BOTAN_P11_ECC_H__ + +#include +#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO) + +#include +#include +#include +#include +#include +#include + +#include + +namespace Botan { +namespace PKCS11 { + +class Session; + +/// Properties for generating a PKCS#11 EC public key +class BOTAN_DLL EC_PublicKeyGenerationProperties final : public PublicKeyProperties + { + public: + /// @param ec_params DER-encoding of an ANSI X9.62 Parameters value + EC_PublicKeyGenerationProperties(const std::vector& ec_params); + + /// @return the DER-encoding of the ec parameters according to ANSI X9.62 + inline const std::vector& ec_params() const + { + return m_ec_params; + } + + private: + const std::vector m_ec_params; + }; + +/// Properties for importing a PKCS#11 EC public key +class BOTAN_DLL EC_PublicKeyImportProperties final : public PublicKeyProperties + { + public: + /** + * @param ec_params DER-encoding of an ANSI X9.62 Parameters value + * @param ec_point DER-encoding of ANSI X9.62 ECPoint value Q + */ + EC_PublicKeyImportProperties(const std::vector& ec_params, const std::vector& ec_point); + + /// @return the DER-encoding of the ec parameters according to ANSI X9.62 + inline const std::vector& ec_params() const + { + return m_ec_params; + } + + /// @return the DER-encoding of the ec public point according to ANSI X9.62 + inline const std::vector& ec_point() const + { + return m_ec_point; + } + + private: + const std::vector m_ec_params; + const std::vector m_ec_point; + }; + +/// Represents a PKCS#11 EC public key +class BOTAN_DLL PKCS11_EC_PublicKey : public virtual EC_PublicKey, + public Object + { + public: + static const ObjectClass Class = ObjectClass::PublicKey; + + /** + * Creates a PKCS11_EC_PublicKey object from an existing PKCS#11 EC public key + * @param session the session to use + * @param handle the handle of the ecc public key + */ + PKCS11_EC_PublicKey(Session& session, ObjectHandle handle); + + /** + * Imports an EC public key + * @param session the session to use + * @param props the attributes of the public key + */ + PKCS11_EC_PublicKey(Session& session, const EC_PublicKeyImportProperties& props); + + size_t max_input_bits() const override; + }; + +/// Properties for generating a PKCS#11 EC private key +class BOTAN_DLL EC_PrivateKeyGenerationProperties final : public PrivateKeyProperties + { + public: + EC_PrivateKeyGenerationProperties() + : PrivateKeyProperties(KeyType::Ec) + {} + }; + +/// Properties for importing a PKCS#11 EC private key +class BOTAN_DLL EC_PrivateKeyImportProperties final : public PrivateKeyProperties + { + public: + /** + * @param ec_params DER-encoding of an ANSI X9.62 Parameters value + * @param value ANSI X9.62 private value d + */ + EC_PrivateKeyImportProperties(const std::vector& ec_params, const BigInt& value); + + /// @return the DER-encoding of the ec parameters according to ANSI X9.62 + inline const std::vector& ec_params() const + { + return m_ec_params; + } + + /// @return the value of the ec private key + inline const BigInt& value() const + { + return m_value; + } + + private: + const std::vector m_ec_params; + const BigInt m_value; + }; + +// note: don't inherit from PKCS11_EC_PublicKey: a private key object IS NOT A public key object on a smartcard (-> two different objects) +// note: don't inherit from EC_PublicKey: the public key can not be extracted from a PKCS11-EC-PrivateKey (its only attributes are CKA_EC_PARAMS and CKA_VALUE) +/// Represents a PKCS#11 EC private key +class BOTAN_DLL PKCS11_EC_PrivateKey : public virtual Private_Key, + public Object + { + public: + static const ObjectClass Class = ObjectClass::PrivateKey; + + /** + * Creates a PKCS11_EC_PrivateKey object from an existing PKCS#11 EC private key + * @param session the session to use + * @param handle the handle of the EC private key + */ + PKCS11_EC_PrivateKey(Session& session, ObjectHandle handle); + + /** + * Imports an EC private key + * @param session the session to use + * @param props the attributes of the private key + */ + PKCS11_EC_PrivateKey(Session& session, const EC_PrivateKeyImportProperties& props); + + /** + * Generates a PKCS#11 EC private key + * @param session the session to use + * @param ec_params DER-encoding of an ANSI X9.62 Parameters value + * @param props the attributes of the private key + * @note no persistent public key object will be created + */ + PKCS11_EC_PrivateKey(Session& session, const std::vector& ec_params, + const EC_PrivateKeyGenerationProperties& props); + + /// @returns the domain of the EC private key + inline const EC_Group& domain() const + { + return m_domain_params; + } + + /** + * Sets the associated public point of this private key + * @param point the public point + * @param point_encoding encoding of the point (default DER-encoded) + */ + void set_public_point(const PointGFp& point, PublicPointEncoding point_encoding = PublicPointEncoding::Der) + { + m_public_key = point; + m_point_encoding = point_encoding; + } + + /** + * Gets the public_point + * @note: the public key must be set using `set_public_point` + * because it is not possible to infer the public key from a PKCS#11 EC private key + * @return the public point of the private key + * @throws Exception if the public point was not set using set_public_point() + */ + + const PointGFp& public_point() const + { + if(m_public_key.is_zero()) + { + throw Exception("Public point not set. Inferring the public key from a PKCS#11 ec private key is not possible."); + } + return m_public_key; + } + + /// @return the encoding format for the public point when it is passed to cryptoki functions as an argument + PublicPointEncoding point_encoding() const + { + return m_point_encoding; + } + + // Private_Key methods + + std::size_t max_input_bits() const override; + + std::vector x509_subject_public_key() const override; + + std::size_t estimated_strength() const override; + + bool check_key(RandomNumberGenerator&, bool) const override; + + AlgorithmIdentifier algorithm_identifier() const override; + + private: + EC_Group m_domain_params; + PointGFp m_public_key; + PublicPointEncoding m_point_encoding; + }; +} + +} + +#endif + +#endif diff --git a/src/lib/prov/pkcs11/p11_ecdh.cpp b/src/lib/prov/pkcs11/p11_ecdh.cpp new file mode 100644 index 000000000..82c1716af --- /dev/null +++ b/src/lib/prov/pkcs11/p11_ecdh.cpp @@ -0,0 +1,151 @@ +/* +* PKCS#11 ECDH +* (C) 2016 Daniel Neus, Sirrix AG +* (C) 2016 Philipp Weber, Sirrix AG +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#if defined(BOTAN_HAS_ECDH) + +#include +#include +#include +#include +#include + +#if defined(BOTAN_HAS_SYSTEM_RNG) + #include +#else + #include +#endif + +namespace Botan { + +namespace PKCS11 { + +ECDH_PublicKey PKCS11_ECDH_PublicKey::export_key() const + { + return ECDH_PublicKey(domain(), public_point()); + } + +ECDH_PrivateKey PKCS11_ECDH_PrivateKey::export_key() const + { + +#if defined(BOTAN_HAS_SYSTEM_RNG) + System_RNG rng; +#else + AutoSeeded_RNG rng; +#endif + auto priv_key = get_attribute_value(AttributeType::Value); + + return ECDH_PrivateKey(rng, domain(), BigInt::decode(priv_key)); + } + +secure_vector PKCS11_ECDH_PrivateKey::pkcs8_private_key() const + { + return export_key().pkcs8_private_key(); + } + +namespace { +class PKCS11_ECDH_KA_Operation : public PK_Ops::Key_Agreement + { + public: + typedef PKCS11_EC_PrivateKey Key_Type; + + static PKCS11_ECDH_KA_Operation* make_ecdh(const Spec& spec, bool use_cofactor) + { + try + { + if(auto* key = dynamic_cast< const PKCS11_EC_PrivateKey* >(&spec.key())) + { + return new PKCS11_ECDH_KA_Operation(*key, spec.padding(), use_cofactor); + } + } + catch(...) + { + } + + return nullptr; + } + + PKCS11_ECDH_KA_Operation(const PKCS11_EC_PrivateKey& key, const std::string& kdf, bool use_cofactor) + : PK_Ops::Key_Agreement(), m_key(key), m_mechanism(MechanismWrapper::create_ecdh_mechanism(kdf, use_cofactor)) + {} + + + /// The encoding in V2.20 was not specified and resulted in different implementations choosing different encodings. + /// Applications relying only on a V2.20 encoding (e.g. the DER variant) other than the one specified now (raw) may not work with all V2.30 compliant tokens. + secure_vector agree(size_t key_len, const byte other_key[], size_t other_key_len, const byte salt[], + size_t salt_len) override + { + std::vector der_encoded_other_key; + if(m_key.point_encoding() == PublicPointEncoding::Der) + { + der_encoded_other_key = DER_Encoder().encode(other_key, other_key_len, OCTET_STRING).get_contents_unlocked(); + m_mechanism.set_ecdh_other_key(der_encoded_other_key.data(), der_encoded_other_key.size()); + } + else + { + m_mechanism.set_ecdh_other_key(other_key, other_key_len); + } + + if(salt != nullptr && salt_len > 0) + { + m_mechanism.set_ecdh_salt(salt, salt_len); + } + + ObjectHandle secret_handle = 0; + AttributeContainer attributes; + attributes.add_bool(AttributeType::Sensitive, false); + attributes.add_bool(AttributeType::Extractable, true); + attributes.add_numeric(AttributeType::Class, static_cast< CK_OBJECT_CLASS >(ObjectClass::SecretKey)); + attributes.add_numeric(AttributeType::KeyType, static_cast< CK_KEY_TYPE >(KeyType::GenericSecret)); + attributes.add_numeric(AttributeType::ValueLen, key_len); + m_key.module()->C_DeriveKey(m_key.session().handle(), m_mechanism.data(), m_key.handle(), attributes.data(), + attributes.count(), &secret_handle); + + Object secret_object(m_key.session(), secret_handle); + secure_vector secret = secret_object.get_attribute_value(AttributeType::Value); + if(secret.size() < key_len) + { + throw PKCS11_Error("ECDH key derivation secret length is too short"); + } + secret.resize(key_len); + return secret; + } + + private: + const PKCS11_EC_PrivateKey& m_key; + MechanismWrapper m_mechanism; + }; + +Algo_Registry::Add g_PKCS11_ECDH_KA_Operation_reg("ECDH", + std::bind(&PKCS11_ECDH_KA_Operation::make_ecdh, std::placeholders::_1, false), "pkcs11", BOTAN_PKCS11_ECDH_PRIO); + +Algo_Registry::Add g_PKCS11_ECDHC_KA_Operation_reg("ECDHC", + std::bind(&PKCS11_ECDH_KA_Operation::make_ecdh, std::placeholders::_1, true), "pkcs11", BOTAN_PKCS11_ECDH_PRIO); + +} + +PKCS11_ECDH_KeyPair generate_ecdh_keypair(Session& session, const EC_PublicKeyGenerationProperties& pub_props, + const EC_PrivateKeyGenerationProperties& priv_props) + { + ObjectHandle pub_key_handle = 0; + ObjectHandle priv_key_handle = 0; + + Mechanism mechanism = { static_cast< CK_MECHANISM_TYPE >(MechanismType::EcKeyPairGen), nullptr, 0 }; + + session.module()->C_GenerateKeyPair(session.handle(), &mechanism, + pub_props.data(), pub_props.count(), priv_props.data(), priv_props.count(), + &pub_key_handle, &priv_key_handle); + + return std::make_pair(PKCS11_ECDH_PublicKey(session, pub_key_handle), PKCS11_ECDH_PrivateKey(session, priv_key_handle)); + } + +} +} + +#endif diff --git a/src/lib/prov/pkcs11/p11_ecdh.h b/src/lib/prov/pkcs11/p11_ecdh.h new file mode 100644 index 000000000..9a73be1c5 --- /dev/null +++ b/src/lib/prov/pkcs11/p11_ecdh.h @@ -0,0 +1,122 @@ +/* +* PKCS#11 ECDH +* (C) 2016 Daniel Neus, Sirrix AG +* (C) 2016 Philipp Weber, Sirrix AG +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_P11_ECDH_H__ +#define BOTAN_P11_ECDH_H__ + +#include +#if defined(BOTAN_HAS_ECDH) + +#include +#include +#include + +#include +#include + +namespace Botan { +namespace PKCS11 { +class Session; + +/// Represents a PKCS#11 ECDH public key +class BOTAN_DLL PKCS11_ECDH_PublicKey final : public PKCS11_EC_PublicKey + { + public: + /** + * Create a PKCS11_ECDH_PublicKey object from an existing PKCS#11 ECDH public key + * @param session the session to use + * @param handle the handle of the ECDH public key + */ + PKCS11_ECDH_PublicKey(Session& session, ObjectHandle handle) + : PKCS11_EC_PublicKey(session, handle) + {} + + /** + * Imports a ECDH public key + * @param session the session to use + * @param props the attributes of the public key + */ + PKCS11_ECDH_PublicKey(Session& session, const EC_PublicKeyImportProperties& props) + : PKCS11_EC_PublicKey(session, props) + {} + + inline std::string algo_name() const override + { + return "ECDH"; + } + + /// @return the exported ECDH public key + ECDH_PublicKey export_key() const; + }; + +/// Represents a PKCS#11 ECDH private key +class BOTAN_DLL PKCS11_ECDH_PrivateKey final : public virtual PKCS11_EC_PrivateKey, public virtual PK_Key_Agreement_Key + { + public: + /** + * Creates a PKCS11_ECDH_PrivateKey object from an existing PKCS#11 ECDH private key + * @param session the session to use + * @param handle the handle of the ECDH private key + */ + PKCS11_ECDH_PrivateKey(Session& session, ObjectHandle handle) + : PKCS11_EC_PrivateKey(session, handle) + {} + + /** + * Imports an ECDH private key + * @param session the session to use + * @param props the attributes of the private key + */ + PKCS11_ECDH_PrivateKey(Session& session, const EC_PrivateKeyImportProperties& props) + : PKCS11_EC_PrivateKey(session, props) + {} + + /** + * Generates a PKCS#11 ECDH private key + * @param session the session to use + * @param ec_params DER-encoding of an ANSI X9.62 Parameters value + * @param props the attributes of the private key + * @note no persistent public key object will be created + */ + PKCS11_ECDH_PrivateKey(Session& session, const std::vector& ec_params, + const EC_PrivateKeyGenerationProperties& props) + : PKCS11_EC_PrivateKey(session, ec_params, props) + {} + + inline std::string algo_name() const override + { + return "ECDH"; + } + + inline std::vector public_value() const override + { + return unlock(EC2OSP(public_point(), PointGFp::UNCOMPRESSED)); + } + + /// @return the exported ECDH private key + ECDH_PrivateKey export_key() const; + + secure_vector pkcs8_private_key() const override; + }; + +using PKCS11_ECDH_KeyPair = std::pair; + +/** +* PKCS#11 ECDH key pair generation +* @param session the session that should be used for the key generation +* @param pub_props the properties of the public key +* @param priv_props the properties of the private key +*/ +BOTAN_DLL PKCS11_ECDH_KeyPair generate_ecdh_keypair(Session& session, const EC_PublicKeyGenerationProperties& pub_props, + const EC_PrivateKeyGenerationProperties& priv_props); +} + +} + +#endif +#endif diff --git a/src/lib/prov/pkcs11/p11_ecdsa.cpp b/src/lib/prov/pkcs11/p11_ecdsa.cpp new file mode 100644 index 000000000..4aeacda72 --- /dev/null +++ b/src/lib/prov/pkcs11/p11_ecdsa.cpp @@ -0,0 +1,239 @@ +/* +* PKCS#11 ECDSA +* (C) 2016 Daniel Neus, Sirrix AG +* (C) 2016 Philipp Weber, Sirrix AG +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#if defined(BOTAN_HAS_ECDSA) + +#include +#include +#include +#include + +#if defined(BOTAN_HAS_SYSTEM_RNG) + #include +#else + #include +#endif + +namespace Botan { +namespace PKCS11 { + +ECDSA_PublicKey PKCS11_ECDSA_PublicKey::export_key() const + { + return ECDSA_PublicKey(domain(), public_point()); + } + +bool PKCS11_ECDSA_PrivateKey::check_key(RandomNumberGenerator& rng, bool strong) const + { + if(!public_point().on_the_curve()) + { + return false; + } + + + if(!strong) + { + return true; + } + + return KeyPair::signature_consistency_check(rng, *this, "EMSA1(SHA-1)"); + } + +ECDSA_PrivateKey PKCS11_ECDSA_PrivateKey::export_key() const + { + +#if defined(BOTAN_HAS_SYSTEM_RNG) + System_RNG rng; +#else + AutoSeeded_RNG rng; +#endif + auto priv_key = get_attribute_value(AttributeType::Value); + + return ECDSA_PrivateKey(rng, domain(), BigInt::decode(priv_key)); + } + +secure_vector PKCS11_ECDSA_PrivateKey::pkcs8_private_key() const + { + return export_key().pkcs8_private_key(); + } + +namespace { + +class PKCS11_ECDSA_Signature_Operation : public PK_Ops::Signature + { + public: + typedef PKCS11_EC_PrivateKey Key_Type; + + PKCS11_ECDSA_Signature_Operation(const PKCS11_EC_PrivateKey& key, const std::string& emsa) + : PK_Ops::Signature(), m_key(key), m_order(key.domain().get_order()), m_mechanism(MechanismWrapper::create_ecdsa_mechanism(emsa)) + {} + + size_t message_parts() const override + { + return 2; + } + + size_t message_part_size() const override + { + return m_order.bytes(); + } + + void update(const byte msg[], size_t msg_len) override + { + if(!m_initialized) + { + // first call to update: initialize and cache message because we can not determine yet whether a single- or multiple-part operation will be performed + m_key.module()->C_SignInit(m_key.session().handle(), m_mechanism.data(), m_key.handle()); + m_initialized = true; + m_first_message = secure_vector(msg, msg + msg_len); + return; + } + + if(!m_first_message.empty()) + { + // second call to update: start multiple-part operation + m_key.module()->C_SignUpdate(m_key.session().handle(), m_first_message); + m_first_message.clear(); + } + + m_key.module()->C_SignUpdate(m_key.session().handle(), const_cast(msg), msg_len); + } + + secure_vector sign(RandomNumberGenerator&) override + { + secure_vector signature; + if(!m_first_message.empty()) + { + // single call to update: perform single-part operation + m_key.module()->C_Sign(m_key.session().handle(), m_first_message, signature); + m_first_message.clear(); + } + else + { + // multiple calls to update (or none): finish multiple-part operation + m_key.module()->C_SignFinal(m_key.session().handle(), signature); + } + m_initialized = false; + return signature; + } + + private: + const PKCS11_EC_PrivateKey& m_key; + const BigInt& m_order; + MechanismWrapper m_mechanism; + secure_vector m_first_message; + bool m_initialized = false; + }; + + +class PKCS11_ECDSA_Verification_Operation : public PK_Ops::Verification + { + public: + typedef PKCS11_EC_PublicKey Key_Type; + + PKCS11_ECDSA_Verification_Operation(const PKCS11_EC_PublicKey& key, const std::string& emsa) + : PK_Ops::Verification(), m_key(key), m_order(key.domain().get_order()), m_mechanism(MechanismWrapper::create_ecdsa_mechanism(emsa)) + {} + + size_t message_parts() const override + { + return 2; + } + + size_t message_part_size() const override + { + return m_order.bytes(); + } + + size_t max_input_bits() const override + { + return m_order.bits(); + } + + void update(const byte msg[], size_t msg_len) override + { + if(!m_initialized) + { + // first call to update: initialize and cache message because we can not determine yet whether a single- or multiple-part operation will be performed + m_key.module()->C_VerifyInit(m_key.session().handle(), m_mechanism.data(), m_key.handle()); + m_initialized = true; + m_first_message = secure_vector(msg, msg + msg_len); + return; + } + + if(!m_first_message.empty()) + { + // second call to update: start multiple-part operation + m_key.module()->C_VerifyUpdate(m_key.session().handle(), m_first_message); + m_first_message.clear(); + } + + m_key.module()->C_VerifyUpdate(m_key.session().handle(), const_cast(msg), msg_len); + } + + bool is_valid_signature(const byte sig[], size_t sig_len) override + { + ReturnValue return_value = ReturnValue::SignatureInvalid; + if(!m_first_message.empty()) + { + // single call to update: perform single-part operation + m_key.module()->C_Verify(m_key.session().handle(), m_first_message.data(), m_first_message.size(), + const_cast(sig), sig_len, &return_value); + m_first_message.clear(); + } + else + { + // multiple calls to update (or none): finish multiple-part operation + m_key.module()->C_VerifyFinal(m_key.session().handle(), const_cast(sig), sig_len, &return_value); + } + m_initialized = false; + if(return_value != ReturnValue::OK && return_value != ReturnValue::SignatureInvalid) + { + throw PKCS11_ReturnError(return_value); + } + return return_value == ReturnValue::OK; + } + + private: + const PKCS11_EC_PublicKey& m_key; + const BigInt& m_order; + MechanismWrapper m_mechanism; + secure_vector m_first_message; + bool m_initialized = false; + }; + +BOTAN_REGISTER_TYPE(PK_Ops::Signature, PKCS11_ECDSA_Signature_Operation, "ECDSA", + (make_pk_op), "pkcs11", BOTAN_PKCS11_ECDSA_PRIO); + +BOTAN_REGISTER_TYPE(PK_Ops::Verification, PKCS11_ECDSA_Verification_Operation, "ECDSA", + (make_pk_op), "pkcs11", BOTAN_PKCS11_ECDSA_PRIO); + +} + +PKCS11_ECDSA_KeyPair generate_ecdsa_keypair(Session& session, const EC_PublicKeyGenerationProperties& pub_props, + const EC_PrivateKeyGenerationProperties& priv_props) + { + ObjectHandle pub_key_handle = 0; + ObjectHandle priv_key_handle = 0; + + Mechanism mechanism = { static_cast(MechanismType::EcKeyPairGen), nullptr, 0 }; + + session.module()->C_GenerateKeyPair(session.handle(), &mechanism, + pub_props.data(), pub_props.count(), priv_props.data(), priv_props.count(), + &pub_key_handle, &priv_key_handle); + + return std::make_pair(PKCS11_ECDSA_PublicKey(session, pub_key_handle), PKCS11_ECDSA_PrivateKey(session, + priv_key_handle)); + } + +} + +} + +#endif diff --git a/src/lib/prov/pkcs11/p11_ecdsa.h b/src/lib/prov/pkcs11/p11_ecdsa.h new file mode 100644 index 000000000..2ac59e028 --- /dev/null +++ b/src/lib/prov/pkcs11/p11_ecdsa.h @@ -0,0 +1,127 @@ +/* +* PKCS#11 ECDSA +* (C) 2016 Daniel Neus, Sirrix AG +* (C) 2016 Philipp Weber, Sirrix AG +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_P11_ECDSA_H__ +#define BOTAN_P11_ECDSA_H__ + +#include +#if defined(BOTAN_HAS_ECDSA) + +#include +#include + +#include + +namespace Botan { +namespace PKCS11 { +class Session; + +/// Represents a PKCS#11 ECDSA public key +class BOTAN_DLL PKCS11_ECDSA_PublicKey final : public PKCS11_EC_PublicKey, public virtual ECDSA_PublicKey + { + public: + /** + * Creates a PKCS11_ECDSA_PublicKey object from an existing PKCS#11 ECDSA public key + * @param session the session to use + * @param handle the handle of the ECDSA public key + */ + PKCS11_ECDSA_PublicKey(Session& session, ObjectHandle handle) + : PKCS11_EC_PublicKey(session, handle) + {} + + /** + * Imports an ECDSA public key + * @param session the session to use + * @param props the attributes of the public key + */ + PKCS11_ECDSA_PublicKey(Session& session, const EC_PublicKeyImportProperties& props) + : PKCS11_EC_PublicKey(session, props) + {} + + inline std::string algo_name() const override + { + return "ECDSA"; + } + + inline std::size_t max_input_bits() const override + { + return domain().get_order().bits(); + } + + /// @return the exported ECDSA public key + ECDSA_PublicKey export_key() const; + }; + +/// Represents a PKCS#11 ECDSA private key +class BOTAN_DLL PKCS11_ECDSA_PrivateKey final : public PKCS11_EC_PrivateKey + { + public: + /** + * Creates a PKCS11_ECDSA_PrivateKey object from an existing PKCS#11 ECDSA private key + * @param session the session to use + * @param handle the handle of the ECDSA private key + */ + PKCS11_ECDSA_PrivateKey(Session& session, ObjectHandle handle) + : PKCS11_EC_PrivateKey(session, handle) + {} + + /** + * Imports a ECDSA private key + * @param session the session to use + * @param props the attributes of the private key + */ + PKCS11_ECDSA_PrivateKey(Session& session, const EC_PrivateKeyImportProperties& props) + : PKCS11_EC_PrivateKey(session, props) + {} + + /** + * Generates a PKCS#11 ECDSA private key + * @param session the session to use + * @param ec_params DER-encoding of an ANSI X9.62 Parameters value + * @param props the attributes of the private key + * @note no persistent public key object will be created + */ + PKCS11_ECDSA_PrivateKey(Session& session, const std::vector& ec_params, + const EC_PrivateKeyGenerationProperties& props) + : PKCS11_EC_PrivateKey(session, ec_params, props) + {} + + inline std::string algo_name() const override + { + return "ECDSA"; + } + + inline size_t message_parts() const override + { + return 2; + } + + /// @return the exported ECDSA private key + ECDSA_PrivateKey export_key() const; + + secure_vector pkcs8_private_key() const override; + + bool check_key(RandomNumberGenerator&, bool) const override; + }; + +using PKCS11_ECDSA_KeyPair = std::pair; + +/** +* ECDSA key pair generation +* @param session the session that should be used for the key generation +* @param pub_props the properties of the public key +* @param priv_props the properties of the private key +*/ +BOTAN_DLL PKCS11_ECDSA_KeyPair generate_ecdsa_keypair(Session& session, + const EC_PublicKeyGenerationProperties& pub_props, const EC_PrivateKeyGenerationProperties& priv_props); +} + +} + +#endif +#endif diff --git a/src/lib/prov/pkcs11/p11_mechanism.cpp b/src/lib/prov/pkcs11/p11_mechanism.cpp new file mode 100644 index 000000000..b3cc1c83b --- /dev/null +++ b/src/lib/prov/pkcs11/p11_mechanism.cpp @@ -0,0 +1,250 @@ +/* +* PKCS#11 Mechanism +* (C) 2016 Daniel Neus, Sirrix AG +* (C) 2016 Philipp Weber, Sirrix AG +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include +#include +#include + +#include + +namespace Botan { +namespace PKCS11 { + +namespace { +using PSS_Params = std::tuple; + +// maps a PSS mechanism type to the number of bytes used for the salt, the mechanism type of the underlying hash algorithm and the MGF +static const std::map PssOptions = + { + { MechanismType::RsaPkcsPss, PSS_Params(0, MechanismType::Sha1, MGF::Mgf1Sha1) }, + { MechanismType::Sha1RsaPkcsPss, PSS_Params(20, MechanismType::Sha1, MGF::Mgf1Sha1) }, + { MechanismType::Sha224RsaPkcsPss, PSS_Params(28, MechanismType::Sha224, MGF::Mgf1Sha224) }, + { MechanismType::Sha256RsaPkcsPss, PSS_Params(32, MechanismType::Sha256, MGF::Mgf1Sha256) }, + { MechanismType::Sha384RsaPkcsPss, PSS_Params(48, MechanismType::Sha384, MGF::Mgf1Sha384) }, + { MechanismType::Sha512RsaPkcsPss, PSS_Params(64, MechanismType::Sha512, MGF::Mgf1Sha512) } + }; + +struct MechanismData + { + explicit MechanismData(MechanismType _type) + : type(_type) + {} + + virtual ~MechanismData() = default; + + // the mechanism to perform + MechanismType type; + }; + +struct RSA_SignMechanism : public MechanismData + { + explicit RSA_SignMechanism(MechanismType _type) + : MechanismData(_type), hash(static_cast(0)), mgf(static_cast(0)), salt_size(0) + { + auto pss_option = PssOptions.find(type); + if(pss_option != PssOptions.end()) + { + hash = std::get<1>(pss_option->second); + mgf = std::get<2>(pss_option->second); + salt_size = std::get<0>(pss_option->second); + } + } + + // hash algorithm used in the PSS encoding; if the signature mechanism does not include message hashing, + // then this value must be the mechanism used by the application to generate the message hash; + // if the signature mechanism includes hashing, then this value must match the hash algorithm indicated by the signature mechanism + MechanismType hash; + + // mask generation function to use on the encoded block + MGF mgf; + + // length, in bytes, of the salt value used in the PSS encoding; typical values are the length of the message hash and zero + size_t salt_size; + }; + +// note: when updating this map, update the documentation for `MechanismWrapper::create_rsa_sign_mechanism` +static std::map SignMechanisms = + { + { "Raw", RSA_SignMechanism(MechanismType::RsaX509) }, + + { "EMSA2(Raw)", RSA_SignMechanism(MechanismType::RsaX931) }, + { "EMSA2(SHA-1)", RSA_SignMechanism(MechanismType::Sha1RsaX931) }, + + // RSASSA PKCS#1 v1.5 + { "EMSA3(Raw)", RSA_SignMechanism(MechanismType::RsaPkcs) }, + { "EMSA3(SHA-1)", RSA_SignMechanism(MechanismType::Sha1RsaPkcs) }, + { "EMSA3(SHA-224)", RSA_SignMechanism(MechanismType::Sha224RsaPkcs) }, + { "EMSA3(SHA-256)", RSA_SignMechanism(MechanismType::Sha256RsaPkcs) }, + { "EMSA3(SHA-384)", RSA_SignMechanism(MechanismType::Sha384RsaPkcs) }, + { "EMSA3(SHA-512)", RSA_SignMechanism(MechanismType::Sha512RsaPkcs) }, + + // RSASSA PKCS#1 PSS + { "EMSA4(Raw)", RSA_SignMechanism(MechanismType::RsaPkcsPss) }, + { "EMSA4(SHA-1)", RSA_SignMechanism(MechanismType::Sha1RsaPkcsPss) }, + { "EMSA4(SHA-224)", RSA_SignMechanism(MechanismType::Sha224RsaPkcsPss) }, + { "EMSA4(SHA-256)", RSA_SignMechanism(MechanismType::Sha256RsaPkcsPss) }, + { "EMSA4(SHA-384)", RSA_SignMechanism(MechanismType::Sha384RsaPkcsPss) }, + { "EMSA4(SHA-512)", RSA_SignMechanism(MechanismType::Sha512RsaPkcsPss) }, + + { "ISO9796", RSA_SignMechanism(MechanismType::Rsa9796) } + }; + +struct RSA_CryptMechanism : public MechanismData + { + RSA_CryptMechanism(MechanismType _type, size_t _padding_size, MechanismType _hash, MGF _mgf) + : MechanismData(_type), hash(_hash), mgf(_mgf), padding_size(_padding_size) + {} + + RSA_CryptMechanism(MechanismType _type, size_t _padding_size) + : RSA_CryptMechanism(_type, _padding_size, static_cast(0), static_cast(0)) + {} + + // mechanism ID of the message digest algorithm used to calculate the digest of the encoding parameter + MechanismType hash; + + // mask generation function to use on the encoded block + MGF mgf; + + // number of bytes required for the padding + size_t padding_size; + }; + +// note: when updating this map, update the documentation for `MechanismWrapper::create_rsa_crypt_mechanism` +static const std::map CryptMechanisms = + { + { "Raw", RSA_CryptMechanism(MechanismType::RsaX509, 0) }, + { "EME-PKCS1-v1_5", RSA_CryptMechanism(MechanismType::RsaPkcs, 11) }, + { "OAEP(SHA-1)", RSA_CryptMechanism(MechanismType::RsaPkcsOaep, 2 + 2 * 20, MechanismType::Sha1, MGF::Mgf1Sha1) }, + { "OAEP(SHA-224)", RSA_CryptMechanism(MechanismType::RsaPkcsOaep, 2 + 2 * 28, MechanismType::Sha224, MGF::Mgf1Sha224) }, + { "OAEP(SHA-256)", RSA_CryptMechanism(MechanismType::RsaPkcsOaep, 2 + 2 * 32, MechanismType::Sha256, MGF::Mgf1Sha256) }, + { "OAEP(SHA-384)", RSA_CryptMechanism(MechanismType::RsaPkcsOaep, 2 + 2 * 48, MechanismType::Sha384, MGF::Mgf1Sha384) }, + { "OAEP(SHA-512)", RSA_CryptMechanism(MechanismType::RsaPkcsOaep, 2 + 2 * 64, MechanismType::Sha512, MGF::Mgf1Sha512) } + }; + +// note: when updating this map, update the documentation for `MechanismWrapper::create_ecdsa_mechanism` +static std::map EcdsaHash = + { + { "Raw", MechanismType::Ecdsa }, + { "SHA-160", MechanismType::EcdsaSha1 }, + { "SHA-224", MechanismType::EcdsaSha224 }, + { "SHA-256", MechanismType::EcdsaSha256 }, + { "SHA-384", MechanismType::EcdsaSha384 }, + { "SHA-512", MechanismType::EcdsaSha512 } + }; + +// note: when updating this map, update the documentation for `MechanismWrapper::create_ecdh_mechanism` +static std::map EcdhHash = + { + { "Raw", KeyDerivation::Null }, + { "SHA-160", KeyDerivation::Sha1Kdf }, + { "SHA-224", KeyDerivation::Sha224Kdf }, + { "SHA-256", KeyDerivation::Sha256Kdf }, + { "SHA-384", KeyDerivation::Sha384Kdf }, + { "SHA-512", KeyDerivation::Sha512Kdf } + }; +} + +MechanismWrapper::MechanismWrapper(MechanismType mechanism_type) + : m_mechanism( { static_cast(mechanism_type), nullptr, 0 }), m_parameters(nullptr) + {} + +MechanismWrapper MechanismWrapper::create_rsa_crypt_mechanism(const std::string& padding) + { + auto mechanism_info_it = CryptMechanisms.find(padding); + if(mechanism_info_it == CryptMechanisms.end()) + { + // at this point it would be possible to support additional configurations that are not predefined above by parsing `padding` + throw Lookup_Error("PKCS#11 RSA encrypt/decrypt does not support EME " + padding); + } + RSA_CryptMechanism mechanism_info = mechanism_info_it->second; + + MechanismWrapper mech(mechanism_info.type); + if(mechanism_info.type == MechanismType::RsaPkcsOaep) + { + mech.m_parameters = std::make_shared(); + mech.m_parameters->oaep_params.hashAlg = static_cast(mechanism_info.hash); + mech.m_parameters->oaep_params.mgf = static_cast(mechanism_info.mgf); + mech.m_parameters->oaep_params.source = CKZ_DATA_SPECIFIED; + mech.m_parameters->oaep_params.pSourceData = nullptr; + mech.m_parameters->oaep_params.ulSourceDataLen = 0; + mech.m_mechanism.pParameter = mech.m_parameters.get(); + mech.m_mechanism.ulParameterLen = sizeof(RsaPkcsOaepParams); + } + mech.m_padding_size = mechanism_info.padding_size; + return mech; + } + +MechanismWrapper MechanismWrapper::create_rsa_sign_mechanism(const std::string& padding) + { + auto mechanism_info_it = SignMechanisms.find(padding); + if(mechanism_info_it == SignMechanisms.end()) + { + // at this point it would be possible to support additional configurations that are not predefined above by parsing `padding` + throw Lookup_Error("PKCS#11 RSA sign/verify does not support EMSA " + padding); + } + RSA_SignMechanism mechanism_info = mechanism_info_it->second; + + MechanismWrapper mech(mechanism_info.type); + if(PssOptions.find(mechanism_info.type) != PssOptions.end()) + { + mech.m_parameters = std::make_shared(); + mech.m_parameters->pss_params.hashAlg = static_cast(mechanism_info.hash); + mech.m_parameters->pss_params.mgf = static_cast(mechanism_info.mgf); + mech.m_parameters->pss_params.sLen = mechanism_info.salt_size; + mech.m_mechanism.pParameter = mech.m_parameters.get(); + mech.m_mechanism.ulParameterLen = sizeof(RsaPkcsPssParams); + } + return mech; + } + +MechanismWrapper MechanismWrapper::create_ecdsa_mechanism(const std::string& hash) + { + std::string hash_name = hash; + + if(hash_name != "Raw") + { + hash_name = hash_for_deterministic_signature(hash); + } + + auto mechanism_type = EcdsaHash.find(hash_name); + if(mechanism_type == EcdsaHash.end()) + { + throw Lookup_Error("PKCS#11 ECDSA sign/verify does not support " + hash); + } + return MechanismWrapper(mechanism_type->second); + } + +MechanismWrapper MechanismWrapper::create_ecdh_mechanism(const std::string& kdf_name, bool use_cofactor) + { + std::string hash = kdf_name; + + if(kdf_name != "Raw") + { + SCAN_Name kdf_hash(kdf_name); + + if(kdf_hash.arg_count() > 0) + { + hash = kdf_hash.arg(0); + } + } + + auto kdf = EcdhHash.find(hash); + if(kdf == EcdhHash.end()) + { + throw Lookup_Error("PKCS#11 ECDH key derivation does not support KDF " + kdf_name); + } + MechanismWrapper mech(use_cofactor ? MechanismType::Ecdh1CofactorDerive : MechanismType::Ecdh1Derive); + mech.m_parameters = std::make_shared(); + mech.m_parameters->ecdh_params.kdf = static_cast(kdf->second); + mech.m_mechanism.pParameter = mech.m_parameters.get(); + mech.m_mechanism.ulParameterLen = sizeof(Ecdh1DeriveParams); + return mech; + } + +} +} diff --git a/src/lib/prov/pkcs11/p11_mechanism.h b/src/lib/prov/pkcs11/p11_mechanism.h new file mode 100644 index 000000000..5d8c826ee --- /dev/null +++ b/src/lib/prov/pkcs11/p11_mechanism.h @@ -0,0 +1,108 @@ +/* +* PKCS#11 Mechanism +* (C) 2016 Daniel Neus, Sirrix AG +* (C) 2016 Philipp Weber, Sirrix AG +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_P11_MECHANISM_H__ +#define BOTAN_P11_MECHANISM_H__ + +#include + +#include +#include +#include + +namespace Botan { +namespace PKCS11 { + +/** +* Simple class to build and hold the data for a CK_MECHANISM struct +* for RSA (encryption/decryption, signature/verification) +* and EC (ecdsa signature/verification, ecdh key derivation) +*/ +class MechanismWrapper final + { + public: + /// @param mechanism_type the CK_MECHANISM_TYPE for the `mechanism` field of the CK_MECHANISM struct + explicit MechanismWrapper(MechanismType mechanism_type); + + /** + * Creates the CK_MECHANISM data for RSA encryption/decryption + * @param padding supported paddings are Raw (X.509), EME-PKCS1-v1_5 (PKCS#1 v1.5) and OAEP (PKCS#1 OAEP) + */ + static MechanismWrapper create_rsa_crypt_mechanism(const std::string& padding); + + /** + * Creates the CK_MECHANISM data for RSA signature/verification + * @param padding supported paddings are Raw (X.509), EMSA3 (PKCS#1 v1.5), EMSA4 (PKCS#1 PSS), + * EMSA2 (ANSI X9.31) and ISO9796 (ISO/IEC 9796) + */ + static MechanismWrapper create_rsa_sign_mechanism(const std::string& padding); + + /** + * Creates the CK_MECHANISM data for ECDSA signature/verification + * @param hash the hash algorithm used to hash the data to sign. + * supported hash functions are Raw and SHA-160 to SHA-512 + */ + static MechanismWrapper create_ecdsa_mechanism(const std::string& hash); + + /** + * Creates the CK_MECHANISM data for ECDH key derivation (CKM_ECDH1_DERIVE or CKM_ECDH1_COFACTOR_DERIVE) + * @param kdf_name the key derivation function to use. Supported KDFs are Raw and SHA-160 to SHA-512 + * @param use_cofactor true if the cofactor key derivation mechanism should be used + */ + static MechanismWrapper create_ecdh_mechanism(const std::string& kdf_name, bool use_cofactor); + + /// Sets the salt for the ECDH mechanism parameters + inline void set_ecdh_salt(const byte salt[], size_t salt_len) + { + m_parameters->ecdh_params.pSharedData = const_cast(salt); + m_parameters->ecdh_params.ulSharedDataLen = salt_len; + } + + /// Sets the public key of the other party for the ECDH mechanism parameters + inline void set_ecdh_other_key(const byte other_key[], size_t other_key_len) + { + m_parameters->ecdh_params.pPublicData = const_cast(other_key); + m_parameters->ecdh_params.ulPublicDataLen = other_key_len; + } + + /// @return a pointer to the CK_MECHANISM struct that can be passed to the cryptoki functions + inline Mechanism* data() const + { + return const_cast(&m_mechanism); + } + + /// @return the size of the padding in bytes (for encryption/decryption) + inline size_t padding_size() const + { + return m_padding_size; + } + + /// Holds the mechanism parameters for OEAP, PSS and ECDH + union MechanismParameters + { + MechanismParameters() + { + std::memset(this, 0, sizeof(MechanismParameters)); + } + + RsaPkcsOaepParams oaep_params; + RsaPkcsPssParams pss_params; + Ecdh1DeriveParams ecdh_params; + }; + + private: + Mechanism m_mechanism; + std::shared_ptr m_parameters; + size_t m_padding_size = 0; + }; + +} + +} + +#endif diff --git a/src/lib/prov/pkcs11/p11_module.cpp b/src/lib/prov/pkcs11/p11_module.cpp new file mode 100644 index 000000000..4ea3dc56d --- /dev/null +++ b/src/lib/prov/pkcs11/p11_module.cpp @@ -0,0 +1,41 @@ +/* +* PKCS#11 Module +* (C) 2016 Daniel Neus, Sirrix AG +* (C) 2016 Philipp Weber, Sirrix AG +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +namespace Botan { + +namespace PKCS11 { + +Module::Module(const std::string& file_path, C_InitializeArgs init_args) + : m_file_path(file_path) + { + reload(init_args); + } + +Module::~Module() BOTAN_NOEXCEPT + { + m_low_level->C_Finalize(nullptr, nullptr); + } + +void Module::reload(C_InitializeArgs init_args) + { + if(m_low_level) + { + m_low_level->C_Finalize(nullptr); + } + + m_library.reset(new Dynamically_Loaded_Library(m_file_path)); + LowLevel::C_GetFunctionList(*m_library, &m_func_list); + m_low_level.reset(new LowLevel(m_func_list)); + + m_low_level->C_Initialize(&init_args); + } + +} +} diff --git a/src/lib/prov/pkcs11/p11_module.h b/src/lib/prov/pkcs11/p11_module.h new file mode 100644 index 000000000..990458a4d --- /dev/null +++ b/src/lib/prov/pkcs11/p11_module.h @@ -0,0 +1,79 @@ +/* +* PKCS#11 Module +* (C) 2016 Daniel Neus, Sirrix AG +* (C) 2016 Philipp Weber, Sirrix AG +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_P11_MODULE_H__ +#define BOTAN_P11_MODULE_H__ + +#include +#include + +#include +#include + +namespace Botan { +namespace PKCS11 { + +/** +* Loads the PKCS#11 shared library +* Calls C_Initialize on load and C_Finalize on destruction +*/ +class BOTAN_DLL Module final + { + public: + /** + * Loads the shared library and calls C_Initialize + * @param file_path the path to the PKCS#11 shared library + * @param init_args flags to use for `C_Initialize` + */ + Module(const std::string& file_path, C_InitializeArgs init_args = { nullptr, nullptr, nullptr, nullptr, static_cast< CK_FLAGS >(Flag::OsLockingOk), nullptr }); + +/* Microsoft Visual Studio <= 2013 does not support default generated move special member functions. + Everything else we target should support it */ +#if !defined( _MSC_VER ) || ( _MSC_VER >= 1900 ) + Module(Module&& other) = default; + Module& operator=(Module&& other) = default; +#endif + + // Dtor calls C_Finalize(). A copy could be deleted while the origin still exists + // Furthermore std::unique_ptr member -> not copyable + Module(const Module& other) = delete; + Module& operator=(const Module& other) = delete; + + /// Calls C_Finalize() + ~Module() BOTAN_NOEXCEPT; + + /** + * Reloads the module and reinitializes it + * @param init_args flags to use for `C_Initialize` + */ + void reload(C_InitializeArgs init_args = { nullptr, nullptr, nullptr, nullptr, static_cast< CK_FLAGS >(Flag::OsLockingOk), nullptr }); + + inline LowLevel* operator->() const + { + return m_low_level.get(); + } + + /// @return general information about Cryptoki + inline Info get_info() const + { + Info info; + m_low_level->C_GetInfo(&info); + return info; + } + + private: + const std::string m_file_path; + FunctionListPtr m_func_list = nullptr; + std::unique_ptr m_library = nullptr; + std::unique_ptr m_low_level = nullptr; + }; + +} +} + +#endif diff --git a/src/lib/prov/pkcs11/p11_object.cpp b/src/lib/prov/pkcs11/p11_object.cpp new file mode 100644 index 000000000..ef7477284 --- /dev/null +++ b/src/lib/prov/pkcs11/p11_object.cpp @@ -0,0 +1,217 @@ +/* +* PKCS#11 Object +* (C) 2016 Daniel Neus, Sirrix AG +* (C) 2016 Philipp Weber, Sirrix AG +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include + +namespace Botan { + +namespace PKCS11 { + +AttributeContainer::AttributeContainer(ObjectClass object_class) + { + add_class(object_class); + } + +void AttributeContainer::add_class(ObjectClass object_class) + { + m_numerics.push_back(static_cast< uint64_t >(object_class)); + add_attribute(AttributeType::Class, reinterpret_cast< byte* >(&m_numerics.back()), sizeof(ObjectClass)); + } + +void AttributeContainer::add_string(AttributeType attribute, const std::string& value) + { + m_strings.push_back(value); + add_attribute(attribute, reinterpret_cast< const byte* >(m_strings.back().data()), value.size()); + } + +void AttributeContainer::add_binary(AttributeType attribute, const byte* value, size_t length) + { + m_vectors.push_back(secure_vector(value, value + length)); + add_attribute(attribute, reinterpret_cast< const byte* >(m_vectors.back().data()), length); + } + +void AttributeContainer::add_bool(AttributeType attribute, bool value) + { + m_numerics.push_back(value ? True : False); + add_attribute(attribute, reinterpret_cast< byte* >(&m_numerics.back()), sizeof(Bbool)); + } + +void AttributeContainer::add_attribute(AttributeType attribute, const byte* value, uint32_t size) + { + bool exists = false; + // check if the attribute has been added already + for(auto& existing_attribute : m_attributes) + { + if(existing_attribute.type == static_cast< CK_ATTRIBUTE_TYPE >(attribute)) + { + // remove old entries + m_strings.erase(std::remove_if(m_strings.begin(), m_strings.end(), [ &existing_attribute ](const std::string& data) + { + return data.data() == existing_attribute.pValue; + }), m_strings.end()); + + m_numerics.erase(std::remove_if(m_numerics.begin(), m_numerics.end(), [ &existing_attribute ](const uint64_t& data) + { + return &data == existing_attribute.pValue; + }), m_numerics.end()); + + m_vectors.erase(std::remove_if(m_vectors.begin(), + m_vectors.end(), [ &existing_attribute ](const secure_vector& data) + { + return data.data() == existing_attribute.pValue; + }), m_vectors.end()); + + existing_attribute.pValue = const_cast< byte* >(value); + existing_attribute.ulValueLen = size; + exists = true; + break; + } + } + + if(!exists) + { + m_attributes.push_back(Attribute{ static_cast< CK_ATTRIBUTE_TYPE >(attribute), const_cast< byte* >(value), size }); + } + } + +// ==================================================================================================== + +ObjectFinder::ObjectFinder(Session& session, const std::vector& search_template) + : m_session(session), m_search_terminated(false) + { + module()->C_FindObjectsInit(m_session.get().handle(), const_cast< Attribute* >(search_template.data()), + search_template.size()); + } + +ObjectFinder::~ObjectFinder() BOTAN_NOEXCEPT + { + if(m_search_terminated == false) + { + module()->C_FindObjectsFinal(m_session.get().handle(), nullptr); + } + } + +std::vector ObjectFinder::find(uint32_t max_count) const + { + std::vector result(max_count); + Ulong objectCount = 0; + module()->C_FindObjects(m_session.get().handle(), result.data(), max_count, &objectCount); + if(objectCount < max_count) + { + result.resize(objectCount); + } + return result; + } + +void ObjectFinder::finish() + { + module()->C_FindObjectsFinal(m_session.get().handle()); + m_search_terminated = true; + } + +// ==================================================================================================== + +ObjectProperties::ObjectProperties(ObjectClass object_class) + : AttributeContainer(object_class), m_object_class(object_class) + {} + +// ==================================================================================================== + +StorageObjectProperties::StorageObjectProperties(ObjectClass object_class) + : ObjectProperties(object_class) + {} + +// ==================================================================================================== + +DataObjectProperties::DataObjectProperties() + : StorageObjectProperties(ObjectClass::Data) + {} + +// ==================================================================================================== + +CertificateProperties::CertificateProperties(CertificateType cert_type) + : StorageObjectProperties(ObjectClass::Certificate), m_cert_type(cert_type) + { + add_numeric(AttributeType::CertificateType, static_cast< CK_CERTIFICATE_TYPE >(m_cert_type)); + } + +// ==================================================================================================== + +KeyProperties::KeyProperties(ObjectClass object_class, KeyType key_type) + : StorageObjectProperties(object_class), m_key_type(key_type) + { + add_numeric(AttributeType::KeyType, static_cast< CK_ULONG >(m_key_type)); + } + +// ==================================================================================================== + +PublicKeyProperties::PublicKeyProperties(KeyType key_type) + : KeyProperties(ObjectClass::PublicKey, key_type) + {} + +// ==================================================================================================== + +PrivateKeyProperties::PrivateKeyProperties(KeyType key_type) + : KeyProperties(ObjectClass::PrivateKey, key_type) + {} + +// ==================================================================================================== + +SecretKeyProperties::SecretKeyProperties(KeyType key_type) + : KeyProperties(ObjectClass::SecretKey, key_type) + {} + +// ==================================================================================================== + +DomainParameterProperties::DomainParameterProperties(KeyType key_type) + : StorageObjectProperties(ObjectClass::DomainParameters), m_key_type(key_type) + { + add_numeric(AttributeType::KeyType, static_cast< CK_ULONG >(m_key_type)); + } + +// ==================================================================================================== + +Object::Object(Session& session, ObjectHandle handle) + : m_session(session), m_handle(handle) + {} + +Object::Object(Session& session, const ObjectProperties& obj_props) + : m_session(session), m_handle(0) + { + m_session.get().module()->C_CreateObject(m_session.get().handle(), obj_props.data(), obj_props.count(), &m_handle); + } + +secure_vector Object::get_attribute_value(AttributeType attribute) const + { + std::map> attribute_map = { { attribute, secure_vector() } }; + module()->C_GetAttributeValue(m_session.get().handle(), m_handle, attribute_map); + return attribute_map.at(attribute); + } + +void Object::set_attribute_value(AttributeType attribute, const secure_vector& value) const + { + std::map> attribute_map = { { attribute, value } }; + module()->C_SetAttributeValue(m_session.get().handle(), m_handle, attribute_map); + } + +void Object::destroy() const + { + module()->C_DestroyObject(m_session.get().handle(), m_handle); + } + +ObjectHandle Object::copy(const AttributeContainer& modified_attributes) const + { + ObjectHandle copied_handle; + module()->C_CopyObject(m_session.get().handle(), m_handle, modified_attributes.data(), modified_attributes.count(), + &copied_handle); + return copied_handle; + } +} +} diff --git a/src/lib/prov/pkcs11/p11_object.h b/src/lib/prov/pkcs11/p11_object.h new file mode 100644 index 000000000..4a6a54b20 --- /dev/null +++ b/src/lib/prov/pkcs11/p11_object.h @@ -0,0 +1,743 @@ +/* +* PKCS#11 Object +* (C) 2016 Daniel Neus, Sirrix AG +* (C) 2016 Philipp Weber, Sirrix AG +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_P11_OBJECT_H__ +#define BOTAN_P11_OBJECT_H__ + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace Botan { +namespace PKCS11 { + +class Module; + +/// Helper class to build the Attribute / CK_ATTRIBUTE structures +class BOTAN_DLL AttributeContainer + { + public: + AttributeContainer() = default; + + /// @param object_class the class type of this container + AttributeContainer(ObjectClass object_class); + + virtual ~AttributeContainer() = default; + +/* Microsoft Visual Studio <= 2013 does not support default generated move special member functions. + Everything else we target should support it */ +#if !defined( _MSC_VER ) || ( _MSC_VER >= 1900 ) + AttributeContainer(AttributeContainer&& other) = default; + AttributeContainer& operator=(AttributeContainer&& other) = default; +#endif + + // Warning when implementing copy/assignment: m_attributes contains pointers to the other members which must be updated after a copy + AttributeContainer(const AttributeContainer& other) = delete; + AttributeContainer& operator=(const AttributeContainer& other) = delete; + + /// @return the attributes this container contains + inline const std::vector& attributes() const + { + return m_attributes; + } + + inline Attribute* data() const + { + return const_cast< Attribute* >(m_attributes.data()); + } + + /// @return the number of attributes in this container + inline size_t count() const + { + return m_attributes.size(); + } + + /// Add a class attribute (CKA_CLASS / AttributeType::Class) + void add_class(ObjectClass object_class); + + /// Add a string attribute (e.g. CKA_LABEL / AttributeType::Label) + void add_string(AttributeType attribute, const std::string& value); + + /// Add a binary attribute (e.g. CKA_ID / AttributeType::Id) + void add_binary(AttributeType attribute, const byte* value, size_t length); + + /// Add a binary attribute (e.g. CKA_ID / AttributeType::Id) + template + void add_binary(AttributeType attribute, const std::vector& binary) + { + add_binary(attribute, binary.data(), binary.size()); + } + + /// Add a bool attribute (e.g. CKA_SENSITIVE / AttributeType::Sensitive) + void add_bool(AttributeType attribute, bool value); + + /// Add a numeric attribute (e.g. CKA_MODULUS_BITS / AttributeType::ModulusBits) + template + void add_numeric(AttributeType attribute, T value) + { + static_assert(std::is_integral::value, "Numeric value required."); + m_numerics.push_back(static_cast< uint64_t >(value)); + add_attribute(attribute, reinterpret_cast< byte* >(&m_numerics.back()), sizeof(T)); + } + + protected: + /// Add a attribute with the given value and size to the attribute collection `m_attributes` + void add_attribute(AttributeType attribute, const byte* value, uint32_t size); + + private: + std::vector m_attributes; + std::list m_numerics; + std::list m_strings; + std::list> m_vectors; + }; + +/// Manages calls to C_FindObjects* functions (C_FindObjectsInit -> C_FindObjects -> C_FindObjectsFinal) +class BOTAN_DLL ObjectFinder final + { + public: + /** + * Initializes a search for token and session objects that match a template (calls C_FindObjectsInit) + * @param session the session to use for the search + * @param search_template the search_template as a vector of `Attribute` + */ + ObjectFinder(Session& session, const std::vector& search_template); + + ObjectFinder(const ObjectFinder& other) = default; + ObjectFinder& operator=(const ObjectFinder& other) = default; + +/* Microsoft Visual Studio <= 2013 does not support default generated move special member functions. + Everything else we target should support it */ +#if !defined( _MSC_VER ) || ( _MSC_VER >= 1900 ) + ObjectFinder(ObjectFinder&& other) = default; + ObjectFinder& operator=(ObjectFinder&& other) = default; +#endif + + /// Terminates a search for token and session objects (calls C_FindObjectsFinal) + ~ObjectFinder() BOTAN_NOEXCEPT; + + /** + * Starts or continues a search for token and session objects that match a template, obtaining additional object handles (calls C_FindObjects) + * @param max_count maximum amount of object handles to retrieve. Default = 100 + * @return the result of the search as a vector of `ObjectHandle` + */ + std::vector find(std::uint32_t max_count = 100) const; + + /// Finishes the search operation manually to allow a new ObjectFinder to exist + void finish(); + + /// @return the module this `ObjectFinder` belongs to + inline Module& module() const + { + return m_session.get().module(); + } + + private: + const std::reference_wrapper m_session; + bool m_search_terminated; + }; + +/// Common attributes of all objects +class BOTAN_DLL ObjectProperties : public AttributeContainer + { + public: + /// @param object_class the object class of the object + ObjectProperties(ObjectClass object_class); + + /// @return the object class of this object + inline ObjectClass object_class() const + { + return m_object_class; + } + + private: + const ObjectClass m_object_class; + }; + +/// Common attributes of all storage objects +class BOTAN_DLL StorageObjectProperties : public ObjectProperties + { + public: + /// @param object_class the CK_OBJECT_CLASS this storage object belongs to + StorageObjectProperties(ObjectClass object_class); + + /// @param label description of the object (RFC2279 string) + inline void set_label(const std::string& label) + { + add_string(AttributeType::Label, label); + } + + /// @param value if true the object is a token object; otherwise the object is a session object + inline void set_token(bool value) + { + add_bool(AttributeType::Token, value); + } + + /** + * @param value if true the object is a private object; otherwise the object is a public object + * When private, a user may not access the object until the user has been authenticated to the token + */ + inline void set_private(bool value) + { + add_bool(AttributeType::Private, value); + } + + /// @param value if true the object can be modified, otherwise it is read-only + void set_modifiable(bool value) + { + add_bool(AttributeType::Modifiable, value); + } + + /// @param value if true the object can be copied using C_CopyObject + void set_copyable(bool value) + { + add_bool(AttributeType::Copyable, value); + } + + /// @param value if true the object can be destroyed using C_DestroyObject + void set_destroyable(bool value) + { + add_bool(AttributeType::Destroyable, value); + } + }; + +/// Common attributes of all data objects +class BOTAN_DLL DataObjectProperties : public StorageObjectProperties + { + public: + DataObjectProperties(); + + /// @param value description of the application that manages the object (RFC2279 string) + inline void set_application(const std::string& value) + { + add_string(AttributeType::Application, value); + } + + /// @param object_id DER-encoding of the object identifier indicating the data object type + inline void set_object_id(const std::vector& object_id) + { + add_binary(AttributeType::ObjectId, object_id); + } + + /// @param value value of the object + inline void set_value(const secure_vector& value) + { + add_binary(AttributeType::Value, value); + } + }; + +/// Common attributes of all certificate objects +class BOTAN_DLL CertificateProperties : public StorageObjectProperties + { + public: + /// @param cert_type type of certificate + CertificateProperties(CertificateType cert_type); + + /// @param value the certificate can be trusted for the application that it was created (can only be set to true by SO user) + inline void set_trusted(bool value) + { + add_bool(AttributeType::Trusted, value); + } + + /// @param category one of `CertificateCategory` + inline void set_category(CertificateCategory category) + { + add_numeric(AttributeType::CertificateCategory, static_cast< CK_CERTIFICATE_CATEGORY >(category)); + } + + /** + * @param checksum the value of this attribute is derived from the certificate by taking the + * first three bytes of the SHA - 1 hash of the certificate object�s `CKA_VALUE` attribute + */ + inline void set_check_value(const std::vector& checksum) + { + add_binary(AttributeType::CheckValue, checksum); + } + + /// @param date start date for the certificate + inline void set_start_date(Date date) + { + add_binary(AttributeType::StartDate, reinterpret_cast(&date), sizeof(Date)); + } + + /// @param date end date for the certificate + inline void set_end_date(Date date) + { + add_binary(AttributeType::EndDate, reinterpret_cast(&date), sizeof(Date)); + } + + /// @param pubkey_info DER-encoding of the SubjectPublicKeyInfo for the public key contained in this certificate + inline void set_public_key_info(const std::vector& pubkey_info) + { + add_binary(AttributeType::PublicKeyInfo, pubkey_info); + } + + /// @return the certificate type of this certificate object + inline CertificateType cert_type() const + { + return m_cert_type; + } + + private: + const CertificateType m_cert_type; + }; + +/// Common attributes of all key objects +class BOTAN_DLL KeyProperties : public StorageObjectProperties + { + public: + /** + * @param object_class the `CK_OBJECT_CLASS` this key object belongs to + * @param key_type type of key + */ + KeyProperties(ObjectClass object_class, KeyType key_type); + + /// @param id key identifier for key + inline void set_id(const std::vector& id) + { + add_binary(AttributeType::Id, id); + } + + /// @param date start date for the key + inline void set_start_date(Date date) + { + add_binary(AttributeType::StartDate, reinterpret_cast(&date), sizeof(Date)); + } + + /// @param date end date for the key + inline void set_end_date(Date date) + { + add_binary(AttributeType::EndDate, reinterpret_cast(&date), sizeof(Date)); + } + + /// @param value true if key supports key derivation (i.e., if other keys can be derived from this one) + inline void set_derive(bool value) + { + add_bool(AttributeType::Derive, value); + } + + /** + * Sets a list of mechanisms allowed to be used with this key + * Not implemented + */ + inline void set_allowed_mechanisms(const std::vector&) + { + throw Exception("Not implemented (KeyProperties::set_allowed_mechanisms)"); + } + + /// @return the key type of this key object + inline KeyType key_type() const + { + return m_key_type; + } + + private: + const KeyType m_key_type; + }; + +/// Common attributes of all public key objects +class BOTAN_DLL PublicKeyProperties : public KeyProperties + { + public: + /// @param key_type type of key + PublicKeyProperties(KeyType key_type); + + /// @param subject DER-encoding of the key subject name + inline void set_subject(const std::vector& subject) + { + add_binary(AttributeType::Subject, subject); + } + + /// @param value true if the key supports encryption + inline void set_encrypt(bool value) + { + add_bool(AttributeType::Encrypt, value); + } + + /// @param value true if the key supports verification where the signature is an appendix to the data + inline void set_verify(bool value) + { + add_bool(AttributeType::Verify, value); + } + + /// @param value true if the key supports verification where the data is recovered from the signature + inline void set_verify_recover(bool value) + { + add_bool(AttributeType::VerifyRecover, value); + } + + /// @param value true if the key supports wrapping (i.e., can be used to wrap other keys) + inline void set_wrap(bool value) + { + add_bool(AttributeType::Wrap, value); + } + + /** + * @param value true if the key can be trusted for the application that it was created. + * The wrapping key can be used to wrap keys with `CKA_WRAP_WITH_TRUSTED` set to `CK_TRUE` + */ + inline void set_trusted(bool value) + { + add_bool(AttributeType::Trusted, value); + } + + /** + * For wrapping keys + * The attribute template to match against any keys wrapped using this wrapping key. + * Keys that do not match cannot be wrapped + * Not implemented + */ + inline void set_wrap_template(const AttributeContainer&) + { + throw Exception("Not implemented (PublicKeyProperties::set_wrap_template)"); + } + + /// @param pubkey_info DER-encoding of the SubjectPublicKeyInfo for this public key + inline void set_public_key_info(const std::vector& pubkey_info) + { + add_binary(AttributeType::PublicKeyInfo, pubkey_info); + } + }; + +/// Common attributes of all private keys +class BOTAN_DLL PrivateKeyProperties : public KeyProperties + { + public: + /// @param key_type type of key + PrivateKeyProperties(KeyType key_type); + + /// @param subject DER-encoding of the key subject name + inline void set_subject(const std::vector& subject) + { + add_binary(AttributeType::Subject, subject); + } + + /// @param value true if the key is sensitive + inline void set_sensitive(bool value) + { + add_bool(AttributeType::Sensitive, value); + } + + /// @param value true if the key supports decryption + inline void set_decrypt(bool value) + { + add_bool(AttributeType::Decrypt, value); + } + + /// @param value true if the key supports signatures where the signature is an appendix to the data + inline void set_sign(bool value) + { + add_bool(AttributeType::Sign, value); + } + + /// @param value true if the key supports signatures where the data can be recovered from the signature + inline void set_sign_recover(bool value) + { + add_bool(AttributeType::SignRecover, value); + } + + /// @param value true if the key supports unwrapping (i.e., can be used to unwrap other keys) + inline void set_unwrap(bool value) + { + add_bool(AttributeType::Unwrap, value); + } + + /// @param value true if the key is extractable and can be wrapped + inline void set_extractable(bool value) + { + add_bool(AttributeType::Extractable, value); + } + + /// @param value true if the key can only be wrapped with a wrapping key that has `CKA_TRUSTED` set to `CK_TRUE` + inline void set_wrap_with_trusted(bool value) + { + add_bool(AttributeType::WrapWithTrusted, value); + } + + /// @param value If true, the user has to supply the PIN for each use (sign or decrypt) with the key + inline void set_always_authenticate(bool value) + { + add_bool(AttributeType::AlwaysAuthenticate, value); + } + + /** + * For wrapping keys + * The attribute template to apply to any keys unwrapped using this wrapping key. + * Any user supplied template is applied after this template as if the object has already been created + * Not implemented + */ + inline void set_unwrap_template(const AttributeContainer&) + { + throw Exception("Not implemented (PrivateKeyProperties::set_unwrap_template)"); + } + + /// @param pubkey_info DER-encoding of the SubjectPublicKeyInfo for this public key + inline void set_public_key_info(const std::vector& pubkey_info) + { + add_binary(AttributeType::PublicKeyInfo, pubkey_info); + } + }; + +/// Common attributes of all secret (symmetric) keys +class BOTAN_DLL SecretKeyProperties : public KeyProperties + { + public: + /// @param key_type type of key + SecretKeyProperties(KeyType key_type); + + /// @param value true if the key is sensitive + inline void set_sensitive(bool value) + { + add_bool(AttributeType::Sensitive, value); + } + + /// @param value true if the key supports encryption + inline void set_encrypt(bool value) + { + add_bool(AttributeType::Encrypt, value); + } + + /// @param value true if the key supports decryption + inline void set_decrypt(bool value) + { + add_bool(AttributeType::Decrypt, value); + } + + /// @param value true if the key supports signatures where the signature is an appendix to the data + inline void set_sign(bool value) + { + add_bool(AttributeType::Sign, value); + } + + /// @param value true if the key supports verification where the signature is an appendix to the data + inline void set_verify(bool value) + { + add_bool(AttributeType::Verify, value); + } + + /// @param value true if the key supports unwrapping (i.e., can be used to unwrap other keys) + inline void set_unwrap(bool value) + { + add_bool(AttributeType::Unwrap, value); + } + + /// @param value true if the key is extractable and can be wrapped + inline void set_extractable(bool value) + { + add_bool(AttributeType::Extractable, value); + } + + /// @param value true if the key can only be wrapped with a wrapping key that has `CKA_TRUSTED` set to `CK_TRUE` + inline void set_wrap_with_trusted(bool value) + { + add_bool(AttributeType::WrapWithTrusted, value); + } + + /// @param value if true, the user has to supply the PIN for each use (sign or decrypt) with the key + inline void set_always_authenticate(bool value) + { + add_bool(AttributeType::AlwaysAuthenticate, value); + } + + /// @param value true if the key supports wrapping (i.e., can be used to wrap other keys) + inline void set_wrap(bool value) + { + add_bool(AttributeType::Wrap, value); + } + + /** + * @param value the key can be trusted for the application that it was created. + * The wrapping key can be used to wrap keys with `CKA_WRAP_WITH_TRUSTED` set to `CK_TRUE` + */ + inline void set_trusted(bool value) + { + add_bool(AttributeType::Trusted, value); + } + + /// @param checksum the key check value of this key + inline void set_check_value(const std::vector& checksum) + { + add_binary(AttributeType::CheckValue, checksum); + } + + /** + * For wrapping keys + * The attribute template to match against any keys wrapped using this wrapping key. + * Keys that do not match cannot be wrapped + * Not implemented + */ + inline void set_wrap_template(const AttributeContainer&) + { + throw Exception("Not implemented (SecretKeyProperties::set_wrap_template)"); + } + + /** + * For wrapping keys + * The attribute template to apply to any keys unwrapped using this wrapping key + * Any user supplied template is applied after this template as if the object has already been created + * Not Implemented + */ + inline void set_unwrap_template(const AttributeContainer&) + { + throw Exception("Not implemented (SecretKeyProperties::set_unwrap_template)"); + } + }; + +/// Common attributes of domain parameter +class BOTAN_DLL DomainParameterProperties : public StorageObjectProperties + { + public: + /// @param key_type type of key the domain parameters can be used to generate + DomainParameterProperties(KeyType key_type); + + /// @return the key type + inline KeyType key_type() const + { + return m_key_type; + } + + private: + const KeyType m_key_type; + }; + +class BOTAN_DLL Object + { + public: + /** + * Creates an `Object` from an existing PKCS#11 object + * @param session the session the object belongs to + * @param handle handle of the object + */ + + Object(Session& session, ObjectHandle handle); + + /** + * Creates the object + * @param session the session in which the object should be created + * @param obj_props properties of this object + */ + Object(Session& session, const ObjectProperties& obj_props); + + virtual ~Object() = default; + + /// Searches for all objects of the given type that match `search_template` + template + static std::vector search(Session& session, const std::vector& search_template); + + /// Searches for all objects of the given type using the label (`CKA_LABEL`) + template + static std::vector search(Session& session, const std::string& label); + + /// Searches for all objects of the given type using the id (`CKA_ID`) + template + static std::vector search(Session& session, const std::vector& id); + + /// Searches for all objects of the given type using the label (`CKA_LABEL`) and id (`CKA_ID`) + template + static std::vector search(Session& session, const std::string& label, const std::vector& id); + + /// Searches for all objects of the given type + template + static std::vector search(Session& session); + + /// @returns the value of the given attribute (using `C_GetAttributeValue`) + secure_vector get_attribute_value(AttributeType attribute) const; + + /// Sets the given value for the attribute (using `C_SetAttributeValue`) + void set_attribute_value(AttributeType attribute, const secure_vector& value) const; + + /// Destroys the object + void destroy() const; + + /** + * Copies the object + * @param modified_attributes the attributes of the copied object + */ + ObjectHandle copy(const AttributeContainer& modified_attributes) const; + + /// @return the handle of this object. + inline ObjectHandle handle() const + { + return m_handle; + } + + /// @return the session this objects belongs to + inline Session& session() const + { + return m_session; + } + + /// @return the module this object belongs to + inline Module& module() const + { + return m_session.get().module(); + } + protected: + Object(Session& session) + : m_session(session) + {} + + const std::reference_wrapper m_session; + ObjectHandle m_handle; + }; + +template +std::vector Object::search(Session& session, const std::vector& search_template) + { + ObjectFinder finder(session, search_template); + std::vector handles = finder.find(); + std::vector result; + result.reserve(handles.size()); + for(const auto& handle : handles) + { + result.emplace_back(T(session, handle)); + } + return result; + } + +template +std::vector Object::search(Session& session, const std::string& label) + { + AttributeContainer search_template(T::Class); + search_template.add_string(AttributeType::Label, label); + return search(session, search_template.attributes()); + } + +template +std::vector Object::search(Session& session, const std::vector& id) + { + AttributeContainer search_template(T::Class); + search_template.add_binary(AttributeType::Id, id); + return search(session, search_template.attributes()); + } + +template +std::vector Object::search(Session& session, const std::string& label, const std::vector& id) + { + AttributeContainer search_template(T::Class); + search_template.add_string(AttributeType::Label, label); + search_template.add_binary(AttributeType::Id, id); + return search(session, search_template.attributes()); + } + +template +std::vector Object::search(Session& session) + { + return search(session, AttributeContainer(T::Class).attributes()); + } + +} + +} + +#endif diff --git a/src/lib/prov/pkcs11/p11_randomgenerator.cpp b/src/lib/prov/pkcs11/p11_randomgenerator.cpp new file mode 100644 index 000000000..eaf9933c6 --- /dev/null +++ b/src/lib/prov/pkcs11/p11_randomgenerator.cpp @@ -0,0 +1,31 @@ +/* +* PKCS#11 Random Generator +* (C) 2016 Daniel Neus, Sirrix AG +* (C) 2016 Philipp Weber, Sirrix AG +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +namespace Botan { + +namespace PKCS11 { + +PKCS11_RNG::PKCS11_RNG(Session& session) + : m_session(session) + {} + +void PKCS11_RNG::randomize(Botan::byte output[], std::size_t length) + { + module()->C_GenerateRandom(m_session.get().handle(), output, length); + } + +void PKCS11_RNG::add_entropy(const Botan::byte in[], std::size_t length) + { + module()->C_SeedRandom(m_session.get().handle(), const_cast(in), length); + } + +} +} + diff --git a/src/lib/prov/pkcs11/p11_randomgenerator.h b/src/lib/prov/pkcs11/p11_randomgenerator.h new file mode 100644 index 000000000..84673278d --- /dev/null +++ b/src/lib/prov/pkcs11/p11_randomgenerator.h @@ -0,0 +1,70 @@ +/* +* PKCS#11 Random Generator +* (C) 2016 Daniel Neus, Sirrix AG +* (C) 2016 Philipp Weber, Sirrix AG +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_P11_RNG_H__ +#define BOTAN_P11_RNG_H__ + +#include +#include +#include + +#include +#include + +namespace Botan { +namespace PKCS11 { + +class Module; + +/// A random generator that only fetches random from the PKCS#11 RNG +class BOTAN_DLL PKCS11_RNG final : public RandomNumberGenerator + { + public: + /// Initialize the RNG with the PKCS#11 session that provides access to the cryptoki functions + explicit PKCS11_RNG(Session& session); + + void clear() override + {} + + std::string name() const override + { + return "PKCS11_RNG"; + } + + /// Always returns true + bool is_seeded() const override + { + return true; + } + + /// No operation - always returns 0 + size_t reseed_with_sources(Entropy_Sources&, size_t, std::chrono::milliseconds) override + { + return 0; + } + + /// @return the module used by this RNG + inline Module& module() const + { + return m_session.get().module(); + } + + /// Calls `C_GenerateRandom` to generate random data + void randomize(Botan::byte output[], std::size_t length) override; + + /// Calls `C_SeedRandom` to add entropy to the random generation function of the token/middleware + void add_entropy(const Botan::byte in[], std::size_t length) override; + + private: + const std::reference_wrapper m_session; + }; +} + +} + +#endif diff --git a/src/lib/prov/pkcs11/p11_rsa.cpp b/src/lib/prov/pkcs11/p11_rsa.cpp new file mode 100644 index 000000000..331e1d0a7 --- /dev/null +++ b/src/lib/prov/pkcs11/p11_rsa.cpp @@ -0,0 +1,382 @@ +/* +* PKCS#11 RSA +* (C) 2016 Daniel Neus, Sirrix AG +* (C) 2016 Philipp Weber, Sirrix AG +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#if defined(BOTAN_HAS_RSA) + +#include +#include +#include +#include +#include +#include + +#if defined(BOTAN_HAS_SYSTEM_RNG) + #include +#else + #include +#endif + +namespace Botan { + +namespace PKCS11 { + +RSA_PublicKeyImportProperties::RSA_PublicKeyImportProperties(const BigInt& modulus, const BigInt& pub_exponent) + : PublicKeyProperties(KeyType::Rsa), m_modulus(modulus), m_pub_exponent(pub_exponent) + { + add_binary(AttributeType::Modulus, BigInt::encode(m_modulus)); + add_binary(AttributeType::PublicExponent, BigInt::encode(m_pub_exponent)); + } + +RSA_PublicKeyGenerationProperties::RSA_PublicKeyGenerationProperties(Ulong bits) + : PublicKeyProperties(KeyType::Rsa) + { + add_numeric(AttributeType::ModulusBits, bits); + } + +PKCS11_RSA_PublicKey::PKCS11_RSA_PublicKey(Session& session, ObjectHandle handle) + : Object(session, handle) + { + m_n = BigInt::decode(get_attribute_value(AttributeType::Modulus)); + m_e = BigInt::decode(get_attribute_value(AttributeType::PublicExponent)); + } + +PKCS11_RSA_PublicKey::PKCS11_RSA_PublicKey(Session& session, const RSA_PublicKeyImportProperties& pubkey_props) + : RSA_PublicKey(pubkey_props.modulus(), pubkey_props.pub_exponent()), Object(session, pubkey_props) + {} + + +RSA_PrivateKeyImportProperties::RSA_PrivateKeyImportProperties(const BigInt& modulus, const BigInt& priv_exponent) + : PrivateKeyProperties(KeyType::Rsa), m_modulus(modulus), m_priv_exponent(priv_exponent) + { + add_binary(AttributeType::Modulus, BigInt::encode(m_modulus)); + add_binary(AttributeType::PrivateExponent, BigInt::encode(m_priv_exponent)); + } + + +PKCS11_RSA_PrivateKey::PKCS11_RSA_PrivateKey(Session& session, ObjectHandle handle) + : Object(session, handle) + { + m_n = BigInt::decode(get_attribute_value(AttributeType::Modulus)); + m_e = BigInt::decode(get_attribute_value(AttributeType::PublicExponent)); + } + +PKCS11_RSA_PrivateKey::PKCS11_RSA_PrivateKey(Session& session, const RSA_PrivateKeyImportProperties& priv_key_props) + : Object(session, priv_key_props) + { + m_n = priv_key_props.modulus(); + m_e = BigInt::decode(get_attribute_value(AttributeType::PublicExponent)); + } + +PKCS11_RSA_PrivateKey::PKCS11_RSA_PrivateKey(Session& session, uint32_t bits, + const RSA_PrivateKeyGenerationProperties& priv_key_props) + : RSA_PublicKey(), Object(session) + { + RSA_PublicKeyGenerationProperties pub_key_props(bits); + pub_key_props.set_encrypt(true); + pub_key_props.set_verify(true); + pub_key_props.set_token(false); // don't create a persistent public key object + + ObjectHandle pub_key_handle = 0; + m_handle = 0; + Mechanism mechanism = { static_cast< CK_MECHANISM_TYPE >(MechanismType::RsaPkcsKeyPairGen), nullptr, 0 }; + session.module()->C_GenerateKeyPair(session.handle(), &mechanism, + pub_key_props.data(), pub_key_props.count(), priv_key_props.data(), priv_key_props.count(), + &pub_key_handle, &m_handle); + + m_n = BigInt::decode(get_attribute_value(AttributeType::Modulus)); + m_e = BigInt::decode(get_attribute_value(AttributeType::PublicExponent)); + } + +RSA_PrivateKey PKCS11_RSA_PrivateKey::export_key() const + { + auto p = get_attribute_value(AttributeType::Prime1); + auto q = get_attribute_value(AttributeType::Prime2); + auto e = get_attribute_value(AttributeType::PublicExponent); + auto d = get_attribute_value(AttributeType::PrivateExponent); + auto n = get_attribute_value(AttributeType::Modulus); + +#if defined(BOTAN_HAS_SYSTEM_RNG) + System_RNG rng; +#else + AutoSeeded_RNG rng; +#endif + + return RSA_PrivateKey(rng + , BigInt::decode(p) + , BigInt::decode(q) + , BigInt::decode(e) + , BigInt::decode(d) + , BigInt::decode(n)); + } + +secure_vector PKCS11_RSA_PrivateKey::pkcs8_private_key() const + { + return export_key().pkcs8_private_key(); + } + + +namespace { +// note: multiple-part decryption operations (with C_DecryptUpdate/C_DecryptFinal) +// are not supported (PK_Ops::Decryption does not provide an `update` method) +class PKCS11_RSA_Decryption_Operation : public PK_Ops::Decryption + { + public: + typedef PKCS11_RSA_PrivateKey Key_Type; + + PKCS11_RSA_Decryption_Operation(const PKCS11_RSA_PrivateKey& key, const std::string& padding) + : m_key(key), m_mechanism(MechanismWrapper::create_rsa_crypt_mechanism(padding)), + m_powermod(m_key.get_e(), m_key.get_n()), m_blinder(m_key.get_n(), + [ this ](const BigInt& k) { return m_powermod(k); }, + [ this ](const BigInt& k) { return inverse_mod(k, m_key.get_n()); }) + { + m_bits = m_key.get_n().bits() - 1; + } + + size_t max_input_bits() const override + { + return m_bits; + } + + secure_vector decrypt(byte& valid_mask, const byte ciphertext[], size_t ciphertext_len) override + { + valid_mask = 0; + m_key.module()->C_DecryptInit(m_key.session().handle(), m_mechanism.data(), m_key.handle()); + + std::vector encrypted_data(ciphertext, ciphertext + ciphertext_len); + + // blind for RSA/RAW decryption + if(! m_mechanism.padding_size()) + { + encrypted_data = BigInt::encode(m_blinder.blind(BigInt::decode(encrypted_data))); + } + + secure_vector decrypted_data; + m_key.module()->C_Decrypt(m_key.session().handle(), encrypted_data, decrypted_data); + + // Unblind for RSA/RAW decryption + if(!m_mechanism.padding_size()) + { + secure_vector unblinded_data = BigInt::encode_locked(m_blinder.unblind(BigInt::decode(decrypted_data))); + + // pad possible leading zeros that were stripped off during conversion to BigInt + secure_vector padded_result(m_key.get_n().bits() / 8 - unblinded_data.size()); + padded_result.insert(padded_result.end(), unblinded_data.begin(), unblinded_data.end()); + decrypted_data = padded_result; + } + + valid_mask = 0xFF; + return decrypted_data; + } + + private: + const PKCS11_RSA_PrivateKey& m_key; + MechanismWrapper m_mechanism; + size_t m_bits = 0; + Fixed_Exponent_Power_Mod m_powermod; + Blinder m_blinder; + }; + +// note: multiple-part encryption operations (with C_EncryptUpdate/C_EncryptFinal) +// are not supported (PK_Ops::Encryption does not provide an `update` method) +class PKCS11_RSA_Encryption_Operation : public PK_Ops::Encryption + { + public: + typedef PKCS11_RSA_PublicKey Key_Type; + + PKCS11_RSA_Encryption_Operation(const PKCS11_RSA_PublicKey& key, const std::string& padding) + : m_key(key), m_mechanism(MechanismWrapper::create_rsa_crypt_mechanism(padding)) + { + m_bits = 8 * (key.get_n().bytes() - m_mechanism.padding_size()) - 1; + } + + size_t max_input_bits() const override + { + return m_bits; + } + + secure_vector encrypt(const byte msg[], size_t msg_len, RandomNumberGenerator&) override + { + m_key.module()->C_EncryptInit(m_key.session().handle(), m_mechanism.data(), m_key.handle()); + + secure_vector encrytped_data; + m_key.module()->C_Encrypt(m_key.session().handle(), secure_vector(msg, msg + msg_len), encrytped_data); + return encrytped_data; + } + + private: + const PKCS11_RSA_PublicKey& m_key; + MechanismWrapper m_mechanism; + size_t m_bits = 0; + }; + + +class PKCS11_RSA_Signature_Operation : public PK_Ops::Signature + { + public: + typedef PKCS11_RSA_PrivateKey Key_Type; + + PKCS11_RSA_Signature_Operation(const PKCS11_RSA_PrivateKey& key, const std::string& padding) + : m_key(key), m_mechanism(MechanismWrapper::create_rsa_sign_mechanism(padding)) + {} + + size_t message_part_size() const override + { + return m_key.get_n().bytes(); + } + + void update(const byte msg[], size_t msg_len) override + { + if(!m_initialized) + { + // first call to update: initialize and cache message because we can not determine yet whether a single- or multiple-part operation will be performed + m_key.module()->C_SignInit(m_key.session().handle(), m_mechanism.data(), m_key.handle()); + m_initialized = true; + m_first_message = secure_vector(msg, msg + msg_len); + return; + } + + if(!m_first_message.empty()) + { + // second call to update: start multiple-part operation + m_key.module()->C_SignUpdate(m_key.session().handle(), m_first_message); + m_first_message.clear(); + } + + m_key.module()->C_SignUpdate(m_key.session().handle(), const_cast< Byte* >(msg), msg_len); + } + + secure_vector sign(RandomNumberGenerator&) override + { + secure_vector signature; + if(!m_first_message.empty()) + { + // single call to update: perform single-part operation + m_key.module()->C_Sign(m_key.session().handle(), m_first_message, signature); + m_first_message.clear(); + } + else + { + // multiple calls to update (or none): finish multiple-part operation + m_key.module()->C_SignFinal(m_key.session().handle(), signature); + } + m_initialized = false; + return signature; + } + + private: + const PKCS11_RSA_PrivateKey& m_key; + bool m_initialized = false; + secure_vector m_first_message; + MechanismWrapper m_mechanism; + }; + + +class PKCS11_RSA_Verification_Operation : public PK_Ops::Verification + { + public: + typedef PKCS11_RSA_PublicKey Key_Type; + + PKCS11_RSA_Verification_Operation(const PKCS11_RSA_PublicKey& key, const std::string& padding) + : m_key(key), m_mechanism(MechanismWrapper::create_rsa_sign_mechanism(padding)) + {} + + size_t message_part_size() const override + { + return m_key.get_n().bytes(); + } + + size_t max_input_bits() const override + { + return m_key.get_n().bits() - 1; + } + + void update(const byte msg[], size_t msg_len) override + { + if(!m_initialized) + { + // first call to update: initialize and cache message because we can not determine yet whether a single- or multiple-part operation will be performed + m_key.module()->C_VerifyInit(m_key.session().handle(), m_mechanism.data(), m_key.handle()); + m_initialized = true; + m_first_message = secure_vector(msg, msg + msg_len); + return; + } + + if(!m_first_message.empty()) + { + // second call to update: start multiple-part operation + m_key.module()->C_VerifyUpdate(m_key.session().handle(), m_first_message); + m_first_message.clear(); + } + + m_key.module()->C_VerifyUpdate(m_key.session().handle(), const_cast< Byte* >(msg), msg_len); + } + + bool is_valid_signature(const byte sig[], size_t sig_len) override + { + ReturnValue return_value = ReturnValue::SignatureInvalid; + if(!m_first_message.empty()) + { + // single call to update: perform single-part operation + m_key.module()->C_Verify(m_key.session().handle(), m_first_message.data(), m_first_message.size(), + const_cast< Byte* >(sig), sig_len, &return_value); + m_first_message.clear(); + } + else + { + // multiple calls to update (or none): finish multiple-part operation + m_key.module()->C_VerifyFinal(m_key.session().handle(), const_cast< Byte* >(sig), sig_len, &return_value); + } + m_initialized = false; + if(return_value != ReturnValue::OK && return_value != ReturnValue::SignatureInvalid) + { + throw PKCS11_ReturnError(return_value); + } + return return_value == ReturnValue::OK; + } + + private: + const PKCS11_RSA_PublicKey& m_key; + bool m_initialized = false; + secure_vector m_first_message; + MechanismWrapper m_mechanism; + }; + +BOTAN_REGISTER_TYPE(PK_Ops::Decryption, PKCS11_RSA_Decryption_Operation, "RSA", + (make_pk_op), "pkcs11", BOTAN_PKCS11_RSA_PRIO); + +BOTAN_REGISTER_TYPE(PK_Ops::Encryption, PKCS11_RSA_Encryption_Operation, "RSA", + (make_pk_op), "pkcs11", BOTAN_PKCS11_RSA_PRIO); + +BOTAN_REGISTER_TYPE(PK_Ops::Signature, PKCS11_RSA_Signature_Operation, "RSA", + (make_pk_op), "pkcs11", BOTAN_PKCS11_RSA_PRIO); + +BOTAN_REGISTER_TYPE(PK_Ops::Verification, PKCS11_RSA_Verification_Operation, "RSA", + (make_pk_op), "pkcs11", BOTAN_PKCS11_RSA_PRIO); + +} + +PKCS11_RSA_KeyPair generate_rsa_keypair(Session& session, const RSA_PublicKeyGenerationProperties& pub_props, + const RSA_PrivateKeyGenerationProperties& priv_props) + { + ObjectHandle pub_key_handle = 0; + ObjectHandle priv_key_handle = 0; + + Mechanism mechanism = { static_cast< CK_MECHANISM_TYPE >(MechanismType::RsaPkcsKeyPairGen), nullptr, 0 }; + + session.module()->C_GenerateKeyPair(session.handle(), &mechanism, + pub_props.data(), pub_props.count(), priv_props.data(), priv_props.count(), + &pub_key_handle, &priv_key_handle); + + return std::make_pair(PKCS11_RSA_PublicKey(session, pub_key_handle), PKCS11_RSA_PrivateKey(session, priv_key_handle)); + } + +} +} +#endif diff --git a/src/lib/prov/pkcs11/p11_rsa.h b/src/lib/prov/pkcs11/p11_rsa.h new file mode 100644 index 000000000..bf1422dc2 --- /dev/null +++ b/src/lib/prov/pkcs11/p11_rsa.h @@ -0,0 +1,213 @@ +/* +* PKCS#11 RSA +* (C) 2016 Daniel Neus, Sirrix AG +* (C) 2016 Philipp Weber, Sirrix AG +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_P11_RSA_H__ +#define BOTAN_P11_RSA_H__ + +#include +#if defined(BOTAN_HAS_RSA) + +#include +#include +#include +#include + +#include + +namespace Botan { +namespace PKCS11 { + +/// Properties for generating a PKCS#11 RSA public key +class BOTAN_DLL RSA_PublicKeyGenerationProperties final : public PublicKeyProperties + { + public: + /// @param bits length in bits of modulus n + explicit RSA_PublicKeyGenerationProperties(Ulong bits); + + /// @param pub_exponent public exponent e + inline void set_pub_exponent(const BigInt& pub_exponent = BigInt(0x10001)) + { + add_binary(AttributeType::PublicExponent, BigInt::encode(pub_exponent)); + } + + virtual ~RSA_PublicKeyGenerationProperties() = default; + }; + +/// Properties for importing a PKCS#11 RSA public key +class BOTAN_DLL RSA_PublicKeyImportProperties final : public PublicKeyProperties + { + public: + /// @param modulus modulus n + /// @param pub_exponent public exponent e + RSA_PublicKeyImportProperties(const BigInt& modulus, const BigInt& pub_exponent); + + /// @return the modulus + inline const BigInt& modulus() const + { + return m_modulus; + } + + /// @return the public exponent + inline const BigInt& pub_exponent() const + { + return m_pub_exponent; + } + + virtual ~RSA_PublicKeyImportProperties() = default; + private: + const BigInt m_modulus; + const BigInt m_pub_exponent; + }; + +/// Represents a PKCS#11 RSA public key +class BOTAN_DLL PKCS11_RSA_PublicKey final : public RSA_PublicKey, + public Object + { + public: + static const ObjectClass Class = ObjectClass::PublicKey; + + /** + * Creates a PKCS11_RSA_PublicKey object from an existing PKCS#11 RSA public key + * @param session the session to use + * @param handle the handle of the RSA public key + */ + PKCS11_RSA_PublicKey(Session& session, ObjectHandle handle); + + /** + * Imports a RSA public key + * @param session the session to use + * @param pubkey_props the attributes of the public key + */ + PKCS11_RSA_PublicKey(Session& session, const RSA_PublicKeyImportProperties& pubkey_props); + }; + +/// Properties for importing a PKCS#11 RSA private key +class BOTAN_DLL RSA_PrivateKeyImportProperties final : public PrivateKeyProperties + { + public: + /** + * @param modulus modulus n + * @param priv_exponent private exponent d + */ + RSA_PrivateKeyImportProperties(const BigInt& modulus, const BigInt& priv_exponent); + + /// @param pub_exponent public exponent e + inline void set_pub_exponent(const BigInt& pub_exponent) + { + add_binary(AttributeType::PublicExponent, BigInt::encode(pub_exponent)); + } + + /// @param prime1 prime p + inline void set_prime_1(const BigInt& prime1) + { + add_binary(AttributeType::Prime1, BigInt::encode(prime1)); + } + + /// @param prime2 prime q + inline void set_prime_2(const BigInt& prime2) + { + add_binary(AttributeType::Prime2, BigInt::encode(prime2)); + } + + /// @param exp1 private exponent d modulo p-1 + inline void set_exponent_1(const BigInt& exp1) + { + add_binary(AttributeType::Exponent1, BigInt::encode(exp1)); + } + + /// @param exp2 private exponent d modulo q-1 + inline void set_exponent_2(const BigInt& exp2) + { + add_binary(AttributeType::Exponent2, BigInt::encode(exp2)); + } + + /// @param coeff CRT coefficient q^-1 mod p + inline void set_coefficient(const BigInt& coeff) + { + add_binary(AttributeType::Coefficient, BigInt::encode(coeff)); + } + + /// @return the modulus + inline const BigInt& modulus() const + { + return m_modulus; + } + + /// @return the private exponent + inline const BigInt& priv_exponent() const + { + return m_priv_exponent; + } + + virtual ~RSA_PrivateKeyImportProperties() = default; + + private: + const BigInt m_modulus; + const BigInt m_priv_exponent; + }; + +/// Properties for generating a PKCS#11 RSA private key +class BOTAN_DLL RSA_PrivateKeyGenerationProperties final : public PrivateKeyProperties + { + public: + RSA_PrivateKeyGenerationProperties() + : PrivateKeyProperties(KeyType::Rsa) + {} + + virtual ~RSA_PrivateKeyGenerationProperties() = default; + }; + +/// Represents a PKCS#11 RSA private key +class BOTAN_DLL PKCS11_RSA_PrivateKey final : public Private_Key, + public RSA_PublicKey, + public Object + { + public: + static const ObjectClass Class = ObjectClass::PrivateKey; + + /// Creates a PKCS11_RSA_PrivateKey object from an existing PKCS#11 RSA private key + PKCS11_RSA_PrivateKey(Session& session, ObjectHandle handle); + + /** + * Imports a RSA private key + * @param session the session to use + * @param priv_key_props the properties of the RSA private key + */ + PKCS11_RSA_PrivateKey(Session& session, const RSA_PrivateKeyImportProperties& priv_key_props); + + /** + * Generates a PKCS#11 RSA private key + * @param session + * @param bits length in bits of modulus n + * @param priv_key_props the properties of the RSA private key + * @note no persistent public key object will be created + */ + PKCS11_RSA_PrivateKey(Session& session, uint32_t bits, const RSA_PrivateKeyGenerationProperties& priv_key_props); + + /// @return the exported RSA private key + RSA_PrivateKey export_key() const; + + secure_vector pkcs8_private_key() const override; + }; + +using PKCS11_RSA_KeyPair = std::pair; + +/** +* RSA key pair generation +* @param session the session that should be used for the key generation +* @param pub_props properties of the public key +* @param priv_props properties of the private key +*/ +BOTAN_DLL PKCS11_RSA_KeyPair generate_rsa_keypair(Session& session, const RSA_PublicKeyGenerationProperties& pub_props, + const RSA_PrivateKeyGenerationProperties& priv_props); +} + +} +#endif + +#endif diff --git a/src/lib/prov/pkcs11/p11_session.cpp b/src/lib/prov/pkcs11/p11_session.cpp new file mode 100644 index 000000000..ceb316169 --- /dev/null +++ b/src/lib/prov/pkcs11/p11_session.cpp @@ -0,0 +1,89 @@ +/* +* PKCS#11 Session +* (C) 2016 Daniel Neus, Sirrix AG +* (C) 2016 Philipp Weber, Sirrix AG +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +namespace Botan { +namespace PKCS11 { + +Session::Session(Slot& slot, bool read_only) + : Session(slot, PKCS11::flags(Flag::SerialSession | (read_only ? Flag::None : Flag::RwSession)), nullptr, nullptr) + {} + +Session::Session(Slot& slot, Flags flags, VoidPtr callback_data, Notify notify_callback) + : m_slot(slot), m_handle(0), m_logged_in(false) + { + module()->C_OpenSession(m_slot.slot_id(), flags, callback_data, notify_callback, &m_handle); + } + +Session::Session(Slot& slot, SessionHandle handle) + : m_slot(slot), m_handle(handle) + { + SessionInfo info = get_info(); + if(info.state == static_cast(SessionState::RoPublicSession) + || info.state == static_cast(SessionState::RwPublicSession)) + { + m_logged_in = false; + } + else + { + m_logged_in = true; + } + } + +Session::~Session() BOTAN_NOEXCEPT + { + if(m_handle) + { + if(m_logged_in) + { + module()->C_Logout(m_handle, nullptr); + } + module()->C_CloseSession(m_handle, nullptr); + m_handle = 0; + } + } + +SessionHandle Session::release() + { + SessionHandle handle = 0; + std::swap(handle, m_handle); + return handle; + } + +void Session::login(UserType user_type, const secure_string& pin) + { + module()->C_Login(m_handle, user_type, pin); + m_logged_in = true; + } + +void Session::logoff() + { + module()->C_Logout(m_handle); + m_logged_in = false; + } + +SessionInfo Session::get_info() const + { + SessionInfo info; + module()->C_GetSessionInfo(m_handle, &info); + return info; + } + +void Session::set_pin(const secure_string& old_pin, const secure_string& new_pin) const + { + module()->C_SetPIN(m_handle, old_pin, new_pin); + } + +void Session::init_pin(const secure_string& new_pin) + { + module()->C_InitPIN(m_handle, new_pin); + } + +} +} diff --git a/src/lib/prov/pkcs11/p11_session.h b/src/lib/prov/pkcs11/p11_session.h new file mode 100644 index 000000000..49f223a90 --- /dev/null +++ b/src/lib/prov/pkcs11/p11_session.h @@ -0,0 +1,105 @@ +/* +* PKCS#11 Session +* (C) 2016 Daniel Neus, Sirrix AG +* (C) 2016 Philipp Weber, Sirrix AG +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_P11_SESSION_H__ +#define BOTAN_P11_SESSION_H__ + +#include +#include + +#include + +namespace Botan { +namespace PKCS11 { +class Module; + +/// Represents a PKCS#11 session +class BOTAN_DLL Session final + { + public: + /** + * @param slot the slot to use + * @param read_only true if the session should be read only, false to create a read-write session + */ + Session(Slot& slot, bool read_only); + + /** + * @param slot the slot to use + * @param flags the flags to use for the session. Remark: Flag::SerialSession is mandatory + * @param callback_data application-defined pointer to be passed to the notification callback + * @param notify_callback address of the notification callback function + */ + Session(Slot& slot, Flags flags, VoidPtr callback_data, Notify notify_callback); + + /// Takes ownership of a session + Session(Slot& slot, SessionHandle handle); + +/* Microsoft Visual Studio <= 2013 does not support default generated move special member functions. + Everything else we target should support it */ +#if !defined( _MSC_VER ) || ( _MSC_VER >= 1900 ) + Session(Session&& other) = default; + Session& operator=(Session&& other) = default; +#endif + + // Dtor calls C_CloseSession() and eventually C_Logout. A copy could close the session while the origin still exists + Session(const Session& other) = delete; + Session& operator=(const Session& other) = delete; + + /// Logout user and close the session on destruction + ~Session() BOTAN_NOEXCEPT; + + /// @return a reference to the slot + inline const Slot& slot() const + { + return m_slot; + } + + /// @return the session handle of this session + inline SessionHandle handle() const + { + return m_handle; + } + + /// @return a reference to the used module + inline Module& module() const + { + return m_slot.module(); + } + + /// @return the released session handle + SessionHandle release(); + + /** + * Login to this session + * @param userType the user type to use for the login + * @param pin the PIN of the user + */ + void login(UserType userType, const secure_string& pin); + + /// Logout from this session + void logoff(); + + /// @return information about this session + SessionInfo get_info() const; + + /// Calls `C_SetPIN` to change the PIN using the old PIN (requires a logged in session) + void set_pin(const secure_string& old_pin, const secure_string& new_pin) const; + + /// Calls `C_InitPIN` to change or initialize the PIN using the SO_PIN (requires a logged in session) + void init_pin(const secure_string& new_pin); + + private: + const Slot& m_slot; + SessionHandle m_handle; + bool m_logged_in; + }; + +} +} + +#endif diff --git a/src/lib/prov/pkcs11/p11_slot.cpp b/src/lib/prov/pkcs11/p11_slot.cpp new file mode 100644 index 000000000..95a0fad50 --- /dev/null +++ b/src/lib/prov/pkcs11/p11_slot.cpp @@ -0,0 +1,60 @@ +/* +* PKCS#11 Slot +* (C) 2016 Daniel Neus, Sirrix AG +* (C) 2016 Philipp Weber, Sirrix AG +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +namespace Botan { + +namespace PKCS11 { + +Slot::Slot(Module& module, SlotId slot_id) + : m_module(module), m_slot_id(slot_id) + {} + +SlotInfo Slot::get_slot_info() const + { + SlotInfo slot_info = {}; + m_module.get()->C_GetSlotInfo(m_slot_id, &slot_info); + return slot_info; + } + +std::vector Slot::get_mechanism_list() const + { + std::vector mechanism_list; + m_module.get()->C_GetMechanismList(m_slot_id, mechanism_list); + return mechanism_list; + } + +MechanismInfo Slot::get_mechanism_info(MechanismType mechanism_type) const + { + MechanismInfo mechanism_info = {}; + m_module.get()->C_GetMechanismInfo(m_slot_id, mechanism_type, &mechanism_info); + return mechanism_info; + } + +std::vector Slot::get_available_slots(Module& module, bool token_present) + { + std::vector slot_vec; + module->C_GetSlotList(token_present, slot_vec); + return slot_vec; + } + +TokenInfo Slot::get_token_info() const + { + TokenInfo token_info; + m_module.get()->C_GetTokenInfo(m_slot_id, &token_info); + return token_info; + } + +void Slot::initialize(const std::string& label, const secure_string& so_pin) const + { + m_module.get()->C_InitToken(m_slot_id, so_pin, label); + } +} + +} diff --git a/src/lib/prov/pkcs11/p11_slot.h b/src/lib/prov/pkcs11/p11_slot.h new file mode 100644 index 000000000..92e585ba1 --- /dev/null +++ b/src/lib/prov/pkcs11/p11_slot.h @@ -0,0 +1,79 @@ +/* +* PKCS#11 Slot +* (C) 2016 Daniel Neus +* (C) 2016 Philipp Weber +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_P11_SLOT_H__ +#define BOTAN_P11_SLOT_H__ + +#include +#include +#include + +#include +#include + +namespace Botan { +namespace PKCS11 { + +/// Represents a PKCS#11 Slot, i.e., a card reader +class BOTAN_DLL Slot final + { + public: + /** + * @param module the PKCS#11 module to use + * @param slot_id the slot id to use + */ + Slot(Module& module, SlotId slot_id); + + /// @return a reference to the module that is used + inline Module& module() const + { + return m_module; + } + + /// @return the slot id + inline SlotId slot_id() const + { + return m_slot_id; + } + + /** + * Get available slots + * @param module the module to use + * @param token_present true if only slots with attached tokens should be returned, false for all slots + * @return a list of available slots (calls C_GetSlotList) + */ + static std::vector get_available_slots(Module& module, bool token_present); + + /// @return information about the slot (`C_GetSlotInfo`) + SlotInfo get_slot_info() const; + + /// Obtains a list of mechanism types supported by the slot (`C_GetMechanismList`) + std::vector get_mechanism_list() const; + + /// Obtains information about a particular mechanism possibly supported by a slot (`C_GetMechanismInfo`) + MechanismInfo get_mechanism_info(MechanismType mechanism_type) const; + + /// Obtains information about a particular token in the system (`C_GetTokenInfo`) + TokenInfo get_token_info() const; + + /** + * Calls `C_InitToken` to initialize the token + * @param label the label for the token (must not exceed 32 bytes according to PKCS#11) + * @param so_pin the PIN of the security officer + */ + void initialize(const std::string& label, const secure_string& so_pin) const; + + private: + const std::reference_wrapper m_module; + const SlotId m_slot_id; + }; + +} +} + +#endif diff --git a/src/lib/prov/pkcs11/p11_x509.cpp b/src/lib/prov/pkcs11/p11_x509.cpp new file mode 100644 index 000000000..76b120368 --- /dev/null +++ b/src/lib/prov/pkcs11/p11_x509.cpp @@ -0,0 +1,37 @@ +/* +* PKCS#11 X.509 +* (C) 2016 Daniel Neus, Sirrix AG +* (C) 2016 Philipp Weber, Sirrix AG +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#if defined(BOTAN_HAS_X509_CERTIFICATES) + +namespace Botan { +namespace PKCS11 { + +X509_CertificateProperties::X509_CertificateProperties(const std::vector& subject, const std::vector& value) + : CertificateProperties(CertificateType::X509), m_subject(subject), m_value(value) + { + add_binary(AttributeType::Subject, m_subject); + add_binary(AttributeType::Value, m_value); + } + +PKCS11_X509_Certificate::PKCS11_X509_Certificate(Session& session, ObjectHandle handle) + : Object(session, handle), X509_Certificate(unlock(get_attribute_value(AttributeType::Value))) + { + } + +PKCS11_X509_Certificate::PKCS11_X509_Certificate(Session& session, const X509_CertificateProperties& props) + : Object(session, props), X509_Certificate(props.value()) + { + } + +} + +} + +#endif diff --git a/src/lib/prov/pkcs11/p11_x509.h b/src/lib/prov/pkcs11/p11_x509.h new file mode 100644 index 000000000..f0e025ff4 --- /dev/null +++ b/src/lib/prov/pkcs11/p11_x509.h @@ -0,0 +1,115 @@ +/* +* PKCS#11 X.509 +* (C) 2016 Daniel Neus, Sirrix AG +* (C) 2016 Philipp Weber, Sirrix AG +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_P11_X509_H__ +#define BOTAN_P11_X509_H__ + +#include +#if defined(BOTAN_HAS_X509_CERTIFICATES) + +#include + +#include + +#include + +namespace Botan { +namespace PKCS11 { + +class Session; + +/// Common attributes of all PKCS#11 X509 certificates +class BOTAN_DLL X509_CertificateProperties final : public CertificateProperties + { + public: + /** + * @param subject DER-encoding of the certificate subject name + * @param value BER-encoding of the certificate + */ + X509_CertificateProperties(const std::vector& subject, const std::vector& value); + + /// @param id key identifier for public/private key pair + inline void set_id(const std::vector& id) + { + add_binary(AttributeType::Id, id); + } + + /// @param issuer DER-encoding of the certificate issuer name + inline void set_issuer(const std::vector& issuer) + { + add_binary(AttributeType::Issuer, issuer); + } + + /// @param serial DER-encoding of the certificate serial number + inline void set_serial(const std::vector& serial) + { + add_binary(AttributeType::SerialNumber, serial); + } + + /// @param hash hash value of the subject public key + inline void set_subject_pubkey_hash(const std::vector& hash) + { + add_binary(AttributeType::HashOfSubjectPublicKey, hash); + } + + /// @param hash hash value of the issuer public key + inline void set_issuer_pubkey_hash(const std::vector& hash) + { + add_binary(AttributeType::HashOfIssuerPublicKey, hash); + } + + /// @param alg defines the mechanism used to calculate `CKA_HASH_OF_SUBJECT_PUBLIC_KEY` and `CKA_HASH_OF_ISSUER_PUBLIC_KEY` + inline void set_hash_alg(MechanismType alg) + { + add_numeric(AttributeType::NameHashAlgorithm, static_cast(alg)); + } + + /// @return the subject + inline const std::vector& subject() const + { + return m_subject; + } + + /// @return the BER-encoding of the certificate + inline const std::vector& value() const + { + return m_value; + } + + private: + const std::vector m_subject; + const std::vector m_value; + }; + +/// Represents a PKCS#11 X509 certificate +class BOTAN_DLL PKCS11_X509_Certificate final : public Object, public X509_Certificate + { + public: + static const ObjectClass Class = ObjectClass::Certificate; + + /** + * Create a PKCS11_X509_Certificate object from an existing PKCS#11 X509 cert + * @param session the session to use + * @param handle the handle of the X.509 certificate + */ + PKCS11_X509_Certificate(Session& session, ObjectHandle handle); + + /** + * Imports a X.509 certificate + * @param session the session to use + * @param props the attributes of the X.509 certificate + */ + PKCS11_X509_Certificate(Session& session, const X509_CertificateProperties& props); + }; + +} +} + +#endif + +#endif diff --git a/src/lib/utils/dyn_load/dyn_load.cpp b/src/lib/utils/dyn_load/dyn_load.cpp index c0795942b..ce6b61a1d 100644 --- a/src/lib/utils/dyn_load/dyn_load.cpp +++ b/src/lib/utils/dyn_load/dyn_load.cpp @@ -5,7 +5,7 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#include +#include #include #include diff --git a/src/lib/utils/dyn_load/dyn_load.h b/src/lib/utils/dyn_load/dyn_load.h index 7a9f4a83c..3a155f3de 100644 --- a/src/lib/utils/dyn_load/dyn_load.h +++ b/src/lib/utils/dyn_load/dyn_load.h @@ -9,13 +9,14 @@ #define BOTAN_DYNAMIC_LOADER_H__ #include +#include namespace Botan { /** * Represents a DLL or shared object */ -class Dynamically_Loaded_Library +class BOTAN_DLL Dynamically_Loaded_Library { public: /** diff --git a/src/lib/utils/dyn_load/info.txt b/src/lib/utils/dyn_load/info.txt index 0cc4e4e73..22a79be43 100644 --- a/src/lib/utils/dyn_load/info.txt +++ b/src/lib/utils/dyn_load/info.txt @@ -1,4 +1,4 @@ -define DYNAMIC_LOADER 20131128 +define DYNAMIC_LOADER 20160310 load_on dep @@ -11,18 +11,12 @@ openbsd qnx solaris windows +darwin android -> dl linux -> dl solaris -> dl +darwin -> dl - - -dyn_load.cpp - - - -dyn_load.h - diff --git a/src/tests/main.cpp b/src/tests/main.cpp index c15fab438..a330bd79a 100644 --- a/src/tests/main.cpp +++ b/src/tests/main.cpp @@ -31,7 +31,7 @@ namespace { class Test_Runner : public Botan_CLI::Command { public: - Test_Runner() : Command("test --threads=0 --soak=5 --drbg-seed= --data-dir= --log-success *suites") {} + Test_Runner() : Command("test --threads=0 --soak=5 --drbg-seed= --data-dir= --pkcs11-lib= --log-success *suites") {} std::string help_text() const override { @@ -73,6 +73,7 @@ class Test_Runner : public Botan_CLI::Command const std::string drbg_seed = get_arg("drbg-seed"); const bool log_success = flag_set("log-success"); const std::string data_dir = get_arg_or("data-dir", "src/tests/data"); + const std::string pkcs11_lib = get_arg("pkcs11-lib"); std::vector req = get_arg_list("suites"); @@ -88,6 +89,19 @@ class Test_Runner : public Botan_CLI::Command std::set all_others = Botan_Tests::Test::registered_tests(); + // do not run pkcs11 tests by default + for(std::set::iterator iter = all_others.begin(); iter != all_others.end();) + { + if((*iter).find("pkcs11") != std::string::npos) + { + iter = all_others.erase(iter); + } + else + { + ++iter; + } + } + for(auto f : req) { all_others.erase(f); @@ -95,6 +109,11 @@ class Test_Runner : public Botan_CLI::Command req.insert(req.end(), all_others.begin(), all_others.end()); } + else if(req.size() == 1 && req.at(0) == "pkcs11") + { + req = {"pkcs11-manage", "pkcs11-module", "pkcs11-slot", "pkcs11-session", "pkcs11-object", "pkcs11-rsa", + "pkcs11-ecdsa", "pkcs11-ecdh", "pkcs11-rng", "pkcs11-x509"}; + } output() << "Testing " << Botan::version_string() << "\n"; output() << "Starting tests"; @@ -104,6 +123,11 @@ class Test_Runner : public Botan_CLI::Command output() << " soak level:" << soak_level; + if(! pkcs11_lib.empty()) + { + output() << " pkcs11 library:" << pkcs11_lib; + } + std::unique_ptr rng; #if defined(BOTAN_HAS_HMAC_DRBG) && defined(BOTAN_HAS_SHA2_64) @@ -137,7 +161,7 @@ class Test_Runner : public Botan_CLI::Command output() << "\n"; - Botan_Tests::Test::setup_tests(soak_level, log_success, data_dir, rng.get()); + Botan_Tests::Test::setup_tests(soak_level, log_success, data_dir, pkcs11_lib, rng.get()); const size_t failed = run_tests(req, output(), threads); diff --git a/src/tests/test_pkcs11.cpp b/src/tests/test_pkcs11.cpp new file mode 100644 index 000000000..676e3f21a --- /dev/null +++ b/src/tests/test_pkcs11.cpp @@ -0,0 +1,42 @@ +/* +* (C) 2016 Daniel Neus +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "test_pkcs11.h" + +namespace Botan_Tests { +using namespace Botan; +using namespace PKCS11; + +std::vector PKCS11_Test::run_pkcs11_tests(const std::string& name, + std::vector>& fns) + { + std::vector results; + + for(size_t i = 0; i != fns.size(); ++i) + { + try + { + results.push_back(fns[ i ]()); + } + catch(PKCS11_ReturnError& e) + { + results.push_back(Test::Result::Failure(name + " test " + std::to_string(i), e.what())); + + if(e.get_return_value() == ReturnValue::PinIncorrect) + { + break; // Do not continue to not potentially lock the token + } + } + catch(std::exception& e) + { + results.push_back(Test::Result::Failure(name + " test " + std::to_string(i), e.what())); + } + } + + return results; + } + +} diff --git a/src/tests/test_pkcs11.h b/src/tests/test_pkcs11.h new file mode 100644 index 000000000..8606612d3 --- /dev/null +++ b/src/tests/test_pkcs11.h @@ -0,0 +1,50 @@ +/* +* (C) 2016 Daniel Neus +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_TESTS_PKCS11_H__ +#define BOTAN_TESTS_PKCS11_H__ + +#include "tests.h" + +#if defined(BOTAN_HAS_PKCS11) + #include +#endif + +#include + +#include +#include +#include + +namespace Botan_Tests { + +#if defined(BOTAN_HAS_PKCS11) + +// PIN is expected to be set to "123456" prior to running the tests +const std::string PIN = "123456"; +const auto PIN_SECVEC = Botan::PKCS11::secure_string(PIN.begin(), PIN.end()); + +const std::string TEST_PIN = "654321"; +const auto TEST_PIN_SECVEC = Botan::PKCS11::secure_string(TEST_PIN.begin(), TEST_PIN.end()); + +// SO PIN is expected to be set to "12345678" prior to running the tests +const std::string SO_PIN = "12345678"; +const auto SO_PIN_SECVEC = Botan::PKCS11::secure_string(SO_PIN.begin(), SO_PIN.end()); + +const std::string TEST_SO_PIN = "87654321"; +const auto TEST_SO_PIN_SECVEC = Botan::PKCS11::secure_string(TEST_SO_PIN.begin(), TEST_SO_PIN.end()); + +class PKCS11_Test : public Test + { + protected: + static std::vector run_pkcs11_tests(const std::string& name, + std::vector>& fns); + }; + +#endif +} + +#endif diff --git a/src/tests/test_pkcs11_high_level.cpp b/src/tests/test_pkcs11_high_level.cpp new file mode 100644 index 000000000..f68203496 --- /dev/null +++ b/src/tests/test_pkcs11_high_level.cpp @@ -0,0 +1,1509 @@ +/* +* (C) 2016 Daniel Neus +* (C) 2016 Philipp Weber +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "tests.h" +#include "test_pkcs11.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(BOTAN_HAS_PKCS11) + #include + #include + #include + #include + #include + #include +#endif + +#include +#include + +#if defined(BOTAN_HAS_RSA) + #include + #include +#endif + +#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO) + #include + #include + #include + #include + #include + #include +#endif + +#if defined(BOTAN_HAS_X509_CERTIFICATES) + #include +#endif + +#if defined(BOTAN_HAS_AUTO_SEEDING_RNG) + #include +#endif + +#if defined(BOTAN_HAS_HMAC_DRBG) + #include +#endif + +namespace Botan_Tests { + +namespace { + +#if defined(BOTAN_HAS_PKCS11) + +using namespace Botan; +using namespace PKCS11; + +class TestSession + { + public: + explicit TestSession(bool login) : + m_module(new Module(Test::pkcs11_lib())) + { + std::vector slot_vec = Slot::get_available_slots(*m_module, true); + m_slot.reset(new Slot(*m_module, slot_vec.at(0))); + m_session.reset(new Session(*m_slot, false)); + if(login) + { + m_session->login(UserType::User, PIN_SECVEC); + } + } + + inline Module& module() const { return *m_module; } + inline Slot& slot() const { return *m_slot; } + inline Session& session() const { return *m_session; } + + private: + std::unique_ptr m_module = nullptr; + std::unique_ptr m_slot = nullptr; + std::unique_ptr m_session = nullptr; + }; + +/***************************** Module *****************************/ + +Test::Result test_module_ctor() + { + Test::Result result("Module ctor"); + + result.test_throws("Module ctor fails for non existent path", []() + { + Module module("/a/b/c"); + }); + + Module module(Test::pkcs11_lib()); + result.test_success("Module ctor did not throw and completed successfully"); + + return result; + } + +Test::Result test_module_reload() + { + Test::Result result("Module reload"); + + Module module(Test::pkcs11_lib()); + + module.reload(); + result.test_success("Module reload did not throw and completed successfully"); + + module.get_info(); + result.test_success("Module get_info() still works after reload"); + + return result; + } + +Test::Result test_multiple_modules() + { + Test::Result result("Module copy"); + Module first_module(Test::pkcs11_lib()); + + result.test_throws("Module ctor fails if module is already initialized", []() + { + Module second_module(Test::pkcs11_lib()); + }); + + return result; + } + +Test::Result test_module_get_info() + { + Test::Result result("Module info"); + + Module module(Test::pkcs11_lib()); + + Info info = module.get_info(); + result.test_ne("Cryptoki version != 0", info.cryptokiVersion.major, 0); + + return result; + } + +class Module_Tests : public PKCS11_Test + { + public: + std::vector run() override + { + std::vector> fns = + { + test_module_ctor, + test_multiple_modules, + test_module_get_info, + test_module_reload + + }; + + return run_pkcs11_tests("Module", fns); + } + }; + +BOTAN_REGISTER_TEST("pkcs11-module", Module_Tests); + +/***************************** Slot *****************************/ + +Test::Result test_slot_get_available_slots() + { + Test::Result result("Slot get_available_slots"); + + Module module(Test::pkcs11_lib()); + std::vector slot_vec = Slot::get_available_slots(module, true); + result.test_gte("Available Slots with attached token >= 1", slot_vec.size(), 1); + + return result; + } + +Test::Result test_slot_ctor() + { + Test::Result result("Slot ctor"); + + Module module(Test::pkcs11_lib()); + std::vector slot_vec = Slot::get_available_slots(module, true); + + Slot slot(module, slot_vec.at(0)); + result.test_success("Slot ctor completed successfully"); + result.test_is_eq(slot.slot_id(), slot_vec.at(0)); + + return result; + } + +Test::Result test_get_slot_info() + { + Test::Result result("Slot get_slot_info"); + + Module module(Test::pkcs11_lib()); + std::vector slot_vec = Slot::get_available_slots(module, true); + Slot slot(module, slot_vec.at(0)); + + SlotInfo info = slot.get_slot_info(); + std::string description = reinterpret_cast< char* >(info.slotDescription); + result.confirm("Slot description is not empty", !description.empty()); + + return result; + } + +Test::Result test_get_token_info() + { + Test::Result result("Slot get_token_info"); + + Module module(Test::pkcs11_lib()); + std::vector slot_vec = Slot::get_available_slots(module, true); + Slot slot(module, slot_vec.at(0)); + + TokenInfo info = slot.get_token_info(); + std::string label = reinterpret_cast< char* >(info.label); + result.confirm("Token label is not empty", ! label.empty()); + + return result; + } + +Test::Result test_get_mechanism_list() + { + Test::Result result("Slot get_mechanism_list"); + + Module module(Test::pkcs11_lib()); + std::vector slot_vec = Slot::get_available_slots(module, true); + Slot slot(module, slot_vec.at(0)); + + std::vector mechanisms = slot.get_mechanism_list(); + result.confirm("The Slot supports at least one mechanism", !mechanisms.empty()); + + return result; + } + +Test::Result test_get_mechanisms_info() + { + Test::Result result("Slot get_mechanism_info"); + + Module module(Test::pkcs11_lib()); + std::vector slot_vec = Slot::get_available_slots(module, true); + Slot slot(module, slot_vec.at(0)); + + slot.get_mechanism_info(MechanismType::RsaPkcsKeyPairGen); + result.test_success("get_mechanism_info() completed successfully."); + + return result; + } + +class Slot_Tests : public PKCS11_Test + { + public: + std::vector run() override + { + std::vector> fns = + { + test_slot_get_available_slots, + test_slot_ctor, + test_get_slot_info, + test_get_token_info, + test_get_mechanism_list, + test_get_mechanisms_info + }; + + return run_pkcs11_tests("Slot", fns); + } + }; + +BOTAN_REGISTER_TEST("pkcs11-slot", Slot_Tests); + +/***************************** Session *****************************/ + +Test::Result test_session_ctor() + { + Test::Result result("Session ctor"); + + Module module(Test::pkcs11_lib()); + std::vector slot_vec = Slot::get_available_slots(module, true); + Slot slot(module, slot_vec.at(0)); + + { + Session read_only_session(slot, true); + result.test_success("read only session opened successfully"); + } + { + Session read_write_session(slot, false); + result.test_success("read write session opened successfully"); + } + { + Flags flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession); + Session read_write_session2(slot, flags, nullptr, nullptr); + result.test_success("read write session with flags param opened successfully"); + } + { + Session read_only_session(slot, true); + Session read_write_session(slot, false); + result.test_success("Opened multiple sessions successfully"); + } + + return result; + } + +Test::Result test_session_release() + { + Test::Result result("Session release/take ownership"); + + Module module(Test::pkcs11_lib()); + std::vector slot_vec = Slot::get_available_slots(module, true); + Slot slot(module, slot_vec.at(0)); + + Session session(slot, false); + SessionHandle handle = session.release(); + + Session session2(slot, handle); + result.test_success("releasing ownership and taking ownership works as expected."); + + return result; + } + +Test::Result test_session_login_logout() + { + Test::Result result("Session login/logout"); + + Module module(Test::pkcs11_lib()); + std::vector slot_vec = Slot::get_available_slots(module, true); + Slot slot(module, slot_vec.at(0)); + + Session session(slot, false); + session.login(UserType::User, PIN_SECVEC); + session.logoff(); + result.test_success("user login/logout succeeded"); + + session.login(UserType::SO, SO_PIN_SECVEC); + result.test_success("SO login succeeded"); + + return result; + } + +Test::Result test_session_info() + { + Test::Result result("Session session info"); + + Module module(Test::pkcs11_lib()); + std::vector slot_vec = Slot::get_available_slots(module, true); + Slot slot(module, slot_vec.at(0)); + + Session session(slot, false); + SessionInfo info = session.get_info(); + result.test_is_eq("slot id is correct", info.slotID, slot_vec.at(0)); + result.test_is_eq("state is a read write public session", info.state, + static_cast(SessionState::RwPublicSession)); + + session.login(UserType::User, PIN_SECVEC); + info = session.get_info(); + result.test_is_eq("state is a read write user session", info.state, + static_cast(SessionState::RwUserFunctions)); + + session.logoff(); + result.test_success("user login/logout succeeded"); + + session.login(UserType::SO, SO_PIN_SECVEC); + result.test_success("SO login succeeded"); + + return result; + } + +class Session_Tests : public PKCS11_Test + { + public: + std::vector run() override + { + std::vector> fns = + { + test_session_ctor, + test_session_release, + test_session_login_logout, + test_session_info + }; + + return run_pkcs11_tests("Session", fns); + } + }; + +BOTAN_REGISTER_TEST("pkcs11-session", Session_Tests); + +/***************************** Object *****************************/ + +Test::Result test_attribute_container() + { + Test::Result result("AttributeContainer"); + + AttributeContainer attributes; + attributes.add_class(ObjectClass::PrivateKey); + + std::string label("test"); + attributes.add_string(AttributeType::Label, label); + + std::vector bin(4); + attributes.add_binary(AttributeType::Value, bin); + + attributes.add_bool(AttributeType::Sensitive, true); + attributes.add_numeric(AttributeType::ObjectId, 12); + + result.test_eq("Five elements in attribute container", attributes.count(), 5); + + return result; + } + +Test::Result test_create_destroy_data_object() + { + Test::Result result("Object create/delete data object"); + + TestSession test_session(true); + + std::string value_string("test data"); + secure_vector value(value_string.begin(), value_string.end()); + + std::size_t id = 1337; + std::string label = "Botan test data object"; + std::string application = "Botan test application"; + DataObjectProperties data_obj_props; + data_obj_props.set_application(application); + data_obj_props.set_label(label); + data_obj_props.set_value(value); + data_obj_props.set_token(true); + data_obj_props.set_modifiable(true); + data_obj_props.set_object_id(DER_Encoder().encode(id).get_contents_unlocked()); + + Object data_obj(test_session.session(), data_obj_props); + result.test_success("Data object creation was successful"); + + data_obj.destroy(); + result.test_success("Data object deletion was successful"); + + return result; + } + +Test::Result test_get_set_attribute_values() + { + Test::Result result("Object get/set attributes"); + + TestSession test_session(true); + + // create object + std::string value_string("test data"); + secure_vector value(value_string.begin(), value_string.end()); + + std::size_t id = 1337; + std::string label = "Botan test data object"; + std::string application = "Botan test application"; + DataObjectProperties data_obj_props; + data_obj_props.set_application(application); + data_obj_props.set_label(label); + data_obj_props.set_value(value); + data_obj_props.set_token(true); + data_obj_props.set_modifiable(true); + data_obj_props.set_object_id(DER_Encoder().encode(id).get_contents_unlocked()); + Object data_obj(test_session.session(), data_obj_props); + + // get attribute + secure_vector retrieved_label = data_obj.get_attribute_value(AttributeType::Label); + std::string retrieved_label_string(retrieved_label.begin(), retrieved_label.end()); + result.test_eq("label was set correctly", retrieved_label_string, label); + + // set attribute + std::string new_label = "Botan test modified data object label"; + secure_vector new_label_secvec(new_label.begin(), new_label.end()); + data_obj.set_attribute_value(AttributeType::Label, new_label_secvec); + + // get and check attribute + retrieved_label = data_obj.get_attribute_value(AttributeType::Label); + retrieved_label_string = std::string(retrieved_label.begin(), retrieved_label.end()); + result.test_eq("label was modified correctly", retrieved_label_string, new_label); + + data_obj.destroy(); + return result; + } + +Test::Result test_object_finder() + { + Test::Result result("ObjectFinder"); + + TestSession test_session(true); + + // create object + std::string value_string("test data"); + secure_vector value(value_string.begin(), value_string.end()); + + std::size_t id = 1337; + std::string label = "Botan test data object"; + std::string application = "Botan test application"; + DataObjectProperties data_obj_props; + data_obj_props.set_application(application); + data_obj_props.set_label(label); + data_obj_props.set_value(value); + data_obj_props.set_token(true); + data_obj_props.set_modifiable(true); + data_obj_props.set_object_id(DER_Encoder().encode(id).get_contents_unlocked()); + Object data_obj(test_session.session(), data_obj_props); + + // search created object + AttributeContainer search_template; + search_template.add_string(AttributeType::Label, label); + ObjectFinder finder(test_session.session(), search_template.attributes()); + + auto search_result = finder.find(); + result.test_eq("one object found", search_result.size(), 1); + finder.finish(); + + Object obj_found(test_session.session(), search_result.at(0)); + result.test_eq("found the object just created (same application)", + obj_found.get_attribute_value(AttributeType::Application) , data_obj.get_attribute_value(AttributeType::Application)); + + auto search_result2 = Object::search(test_session.session(), search_template.attributes()); + result.test_eq("found the object just created (same label)", obj_found.get_attribute_value(AttributeType::Label), + search_result2.at(0).get_attribute_value(AttributeType::Label)); + + data_obj.destroy(); + return result; + } + +Test::Result test_object_copy() + { + Test::Result result("Object copy"); + + TestSession test_session(true); + + // create object + std::string value_string("test data"); + secure_vector value(value_string.begin(), value_string.end()); + + std::size_t id = 1337; + std::string label = "Botan test data object"; + std::string application = "Botan test application"; + DataObjectProperties data_obj_props; + data_obj_props.set_application(application); + data_obj_props.set_label(label); + data_obj_props.set_value(value); + data_obj_props.set_token(true); + data_obj_props.set_modifiable(true); + data_obj_props.set_object_id(DER_Encoder().encode(id).get_contents_unlocked()); + Object data_obj(test_session.session(), data_obj_props); + + // copy created object + AttributeContainer copy_attributes; + copy_attributes.add_string(AttributeType::Label, "Botan test copied object"); + ObjectHandle copied_obj_handle = data_obj.copy(copy_attributes); + + ObjectFinder searcher(test_session.session(), copy_attributes.attributes()); + auto search_result = searcher.find(); + result.test_eq("one object found", search_result.size(), 1); + + data_obj.destroy(); + + Object copied_obj(test_session.session(), copied_obj_handle); + copied_obj.destroy(); + return result; + } + +class Object_Tests : public PKCS11_Test + { + public: + std::vector run() override + { + std::vector> fns = + { + test_attribute_container, + test_create_destroy_data_object, + test_get_set_attribute_values, + test_object_finder, + test_object_copy + }; + + return run_pkcs11_tests("Object", fns); + } + }; + +BOTAN_REGISTER_TEST("pkcs11-object", Object_Tests); + +/***************************** PKCS11 RSA *****************************/ + +#if defined(BOTAN_HAS_RSA) + +Test::Result test_rsa_privkey_import() + { + Test::Result result("PKCS11 import RSA private key"); + + TestSession test_session(true); + + // create private key + RSA_PrivateKey priv_key(Test::rng(), 2048); + + // import to card + RSA_PrivateKeyImportProperties props(priv_key.get_n(), priv_key.get_d()); + props.set_pub_exponent(priv_key.get_e()); + props.set_prime_1(priv_key.get_p()); + props.set_prime_2(priv_key.get_q()); + props.set_coefficient(priv_key.get_c()); + props.set_exponent_1(priv_key.get_d1()); + props.set_exponent_2(priv_key.get_d2()); + + props.set_token(true); + props.set_private(true); + props.set_decrypt(true); + props.set_sign(true); + + PKCS11_RSA_PrivateKey pk(test_session.session(), props); + result.test_success("RSA private key import was successful"); + + pk.destroy(); + return result; + } + +Test::Result test_rsa_privkey_export() + { + Test::Result result("PKCS11 export RSA private key"); + + TestSession test_session(true); + + // create private key + RSA_PrivateKey priv_key(Test::rng(), 2048); + + // import to card + RSA_PrivateKeyImportProperties props(priv_key.get_n(), priv_key.get_d()); + props.set_pub_exponent(priv_key.get_e()); + props.set_prime_1(priv_key.get_p()); + props.set_prime_2(priv_key.get_q()); + props.set_coefficient(priv_key.get_c()); + props.set_exponent_1(priv_key.get_d1()); + props.set_exponent_2(priv_key.get_d2()); + + props.set_token(true); + props.set_private(true); + props.set_decrypt(true); + props.set_sign(true); + props.set_extractable(true); + props.set_sensitive(false); + + PKCS11_RSA_PrivateKey pk(test_session.session(), props); + + RSA_PrivateKey exported = pk.export_key(); + result.test_success("RSA private key export was successful"); + result.test_eq("pkcs8 private key", pk.pkcs8_private_key(), priv_key.pkcs8_private_key()); + + pk.destroy(); + return result; + } + +Test::Result test_rsa_pubkey_import() + { + Test::Result result("PKCS11 import RSA public key"); + + TestSession test_session(true); + + // create public key from private key + RSA_PrivateKey priv_key(Test::rng(), 2048); + + // import to card + RSA_PublicKeyImportProperties props(priv_key.get_n(), priv_key.get_e()); + props.set_token(true); + props.set_encrypt(true); + props.set_private(false); + + PKCS11_RSA_PublicKey pk(test_session.session(), props); + result.test_success("RSA public key import was successful"); + + pk.destroy(); + + return result; + } + +Test::Result test_rsa_generate_private_key() + { + Test::Result result("PKCS11 generate RSA private key"); + TestSession test_session(true); + + RSA_PrivateKeyGenerationProperties props; + props.set_token(true); + props.set_private(true); + props.set_sign(true); + props.set_decrypt(true); + + PKCS11_RSA_PrivateKey pk(test_session.session(), 2048, props); + result.test_success("RSA private key generation was successful"); + + pk.destroy(); + + return result; + } + +PKCS11_RSA_KeyPair generate_rsa_keypair(const TestSession& test_session) + { + RSA_PublicKeyGenerationProperties pub_props(2048UL); + pub_props.set_pub_exponent(); + pub_props.set_label("BOTAN_TEST_RSA_PUB_KEY"); + pub_props.set_token(true); + pub_props.set_encrypt(true); + pub_props.set_verify(true); + pub_props.set_private(false); + + RSA_PrivateKeyGenerationProperties priv_props; + priv_props.set_label("BOTAN_TEST_RSA_PRIV_KEY"); + priv_props.set_token(true); + priv_props.set_private(true); + priv_props.set_sign(true); + priv_props.set_decrypt(true); + + return PKCS11::generate_rsa_keypair(test_session.session(), pub_props, priv_props); + } + +Test::Result test_rsa_generate_key_pair() + { + Test::Result result("PKCS11 generate RSA key pair"); + TestSession test_session(true); + + PKCS11_RSA_KeyPair keypair = generate_rsa_keypair(test_session); + result.test_success("RSA key pair generation was successful"); + + keypair.first.destroy(); + keypair.second.destroy(); + + return result; + } + +Test::Result test_rsa_encrypt_decrypt() + { + Test::Result result("PKCS11 RSA encrypt decrypt"); + TestSession test_session(true); + + // generate key pair + PKCS11_RSA_KeyPair keypair = generate_rsa_keypair(test_session); + + auto encrypt_and_decrypt = [&keypair, &result](const std::vector& plaintext, const std::string& padding) -> void + { + Botan::PK_Encryptor_EME encryptor(keypair.first, padding, "pkcs11"); + auto encrypted = encryptor.encrypt(plaintext, Test::rng()); + + Botan::PK_Decryptor_EME decryptor(keypair.second, padding, "pkcs11"); + auto decrypted = decryptor.decrypt(encrypted); + + // some token / middlewares do not remove the padding bytes + decrypted.resize(plaintext.size()); + + result.test_eq("RSA PKCS11 encrypt and decrypt: " + padding, decrypted, plaintext); + }; + + std::vector plaintext(256); + std::iota(std::begin(plaintext), std::end(plaintext), 0); + encrypt_and_decrypt(plaintext, "Raw"); + + plaintext = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x00 }; + encrypt_and_decrypt(plaintext, "EME-PKCS1-v1_5"); + + encrypt_and_decrypt(plaintext, "OAEP(SHA-1)"); + + keypair.first.destroy(); + keypair.second.destroy(); + + return result; + } + +Test::Result test_rsa_sign_verify() + { + Test::Result result("PKCS11 RSA sign and verify"); + TestSession test_session(true); + + // generate key pair + PKCS11_RSA_KeyPair keypair = generate_rsa_keypair(test_session); + + std::vector plaintext(256); + std::iota(std::begin(plaintext), std::end(plaintext), 0); + + auto sign_and_verify = [&keypair, &plaintext, &result](const std::string& emsa, bool multipart) -> void + { + Botan::PK_Signer signer(keypair.second, emsa, Botan::IEEE_1363, "pkcs11"); + std::vector signature; + if ( multipart ) + { + signer.update(plaintext.data(), plaintext.size() / 2); + signature = signer.sign_message(plaintext.data() + plaintext.size() / 2, plaintext.size() / 2, Test::rng()); + } + else + { + signature = signer.sign_message(plaintext, Test::rng()); + } + + + Botan::PK_Verifier verifier(keypair.first, emsa, Botan::IEEE_1363, "pkcs11"); + bool rsa_ok = false; + if ( multipart ) + { + verifier.update(plaintext.data(), plaintext.size() / 2); + rsa_ok = verifier.verify_message(plaintext.data() + plaintext.size() / 2, plaintext.size() / 2, signature.data(), signature.size()); + } + else + { + rsa_ok = verifier.verify_message(plaintext, signature); + } + + result.test_eq("RSA PKCS11 sign and verify: " + emsa, rsa_ok, true); + }; + + // single-part sign + sign_and_verify("Raw", false); + sign_and_verify("EMSA3(SHA-256)", false); + sign_and_verify("EMSA4(SHA-256)", false); + + // multi-part sign + sign_and_verify("EMSA3(SHA-256)", true); + sign_and_verify("EMSA4(SHA-256)", true); + + keypair.first.destroy(); + keypair.second.destroy(); + + return result; + } + +class PKCS11_RSA_Tests : public PKCS11_Test + { + public: + std::vector run() override + { + std::vector> fns = + { + test_rsa_privkey_import, + test_rsa_pubkey_import, + test_rsa_privkey_export, + test_rsa_generate_private_key, + test_rsa_generate_key_pair, + test_rsa_encrypt_decrypt, + test_rsa_sign_verify + }; + + return run_pkcs11_tests("PKCS11 RSA", fns); + } + }; + +BOTAN_REGISTER_TEST("pkcs11-rsa", PKCS11_RSA_Tests); +#endif + +/***************************** PKCS11 ECDSA *****************************/ + +#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO) + +Test::Result test_ecdsa_privkey_import() + { + Test::Result result("PKCS11 import ECDSA private key"); + + TestSession test_session(true); + + // create ecdsa private key + ECDSA_PrivateKey priv_key(Test::rng(), EC_Group("secp256r1")); + priv_key.set_parameter_encoding(EC_Group_Encoding::EC_DOMPAR_ENC_OID); + + // import to card + EC_PrivateKeyImportProperties props(priv_key.DER_domain(), priv_key.private_value()); + props.set_token(true); + props.set_private(true); + props.set_sign(true); + + // label + std::string label = "Botan test ecdsa key"; + props.set_label(label); + + PKCS11_ECDSA_PrivateKey pk(test_session.session(), props); + result.test_success("ECDSA private key import was successful"); + + pk.destroy(); + return result; + } + +Test::Result test_ecdsa_privkey_export() + { + Test::Result result("PKCS11 export ECDSA private key"); + + TestSession test_session(true); + + // create private key + ECDSA_PrivateKey priv_key(Test::rng(), EC_Group("secp256r1")); + priv_key.set_parameter_encoding(EC_Group_Encoding::EC_DOMPAR_ENC_OID); + + // import to card + EC_PrivateKeyImportProperties props(priv_key.DER_domain(), priv_key.private_value()); + props.set_token(true); + props.set_private(true); + props.set_sign(true); + props.set_extractable(true); + + // label + std::string label = "Botan test ecdsa key"; + props.set_label(label); + + PKCS11_ECDSA_PrivateKey pk(test_session.session(), props); + + ECDSA_PrivateKey exported = pk.export_key(); + result.test_success("ECDSA private key export was successful"); + + pk.destroy(); + return result; + } + +Test::Result test_ecdsa_pubkey_import() + { + Test::Result result("PKCS11 import ECDSA public key"); + + TestSession test_session(true); + + // create ecdsa private key + ECDSA_PrivateKey priv_key(Test::rng(), EC_Group("secp256r1")); + priv_key.set_parameter_encoding(EC_Group_Encoding::EC_DOMPAR_ENC_OID); + + // import to card + EC_PublicKeyImportProperties props(priv_key.DER_domain(), DER_Encoder().encode(EC2OSP(priv_key.public_point(), + PointGFp::UNCOMPRESSED), OCTET_STRING).get_contents_unlocked()); + props.set_token(true); + props.set_verify(true); + props.set_private(false); + + // label + std::string label = "Botan test ecdsa pub key"; + props.set_label(label); + + PKCS11_ECDSA_PublicKey pk(test_session.session(), props); + result.test_success("ECDSA public key import was successful"); + + pk.destroy(); + return result; + } + +Test::Result test_ecdsa_pubkey_export() + { + Test::Result result("PKCS11 export ECDSA public key"); + + TestSession test_session(true); + + // create public key from private key + ECDSA_PrivateKey priv_key(Test::rng(), EC_Group("secp256r1")); + priv_key.set_parameter_encoding(EC_Group_Encoding::EC_DOMPAR_ENC_OID); + + // import to card + EC_PublicKeyImportProperties props(priv_key.DER_domain(), DER_Encoder().encode(EC2OSP(priv_key.public_point(), + PointGFp::UNCOMPRESSED), OCTET_STRING).get_contents_unlocked()); + props.set_token(true); + props.set_verify(true); + props.set_private(false); + + // label + std::string label = "Botan test ecdsa pub key"; + props.set_label(label); + + PKCS11_ECDSA_PublicKey pk(test_session.session(), props); + + ECDSA_PublicKey exported = pk.export_key(); + result.test_success("ECDSA public key export was successful"); + + pk.destroy(); + + return result; + } + +Test::Result test_ecdsa_generate_private_key() + { + Test::Result result("PKCS11 generate ECDSA private key"); + TestSession test_session(true); + + EC_PrivateKeyGenerationProperties props; + props.set_token(true); + props.set_private(true); + props.set_sign(true); + + PKCS11_ECDSA_PrivateKey pk(test_session.session(), + EC_Group("secp256r1").DER_encode(EC_Group_Encoding::EC_DOMPAR_ENC_OID), props); + result.test_success("ECDSA private key generation was successful"); + + pk.destroy(); + + return result; + } + +PKCS11_ECDSA_KeyPair generate_ecdsa_keypair(const TestSession& test_session) + { + EC_PublicKeyGenerationProperties pub_props(EC_Group("secp256r1").DER_encode( + EC_Group_Encoding::EC_DOMPAR_ENC_OID)); + pub_props.set_label("BOTAN_TEST_ECDSA_PUB_KEY"); + pub_props.set_token(true); + pub_props.set_verify(true); + pub_props.set_private(false); + pub_props.set_modifiable(true); + + EC_PrivateKeyGenerationProperties priv_props; + priv_props.set_label("BOTAN_TEST_ECDSA_PRIV_KEY"); + priv_props.set_token(true); + priv_props.set_private(true); + priv_props.set_sensitive(true); + priv_props.set_extractable(false); + priv_props.set_sign(true); + priv_props.set_modifiable(true); + + return PKCS11::generate_ecdsa_keypair(test_session.session(), pub_props, priv_props); + } + +Test::Result test_ecdsa_generate_keypair() + { + Test::Result result("PKCS11 generate ECDSA key pair"); + TestSession test_session(true); + + PKCS11_ECDSA_KeyPair keypair = generate_ecdsa_keypair(test_session); + result.test_success("ECDSA key pair generation was successful"); + + keypair.first.destroy(); + keypair.second.destroy(); + + return result; + } + +Test::Result test_ecdsa_sign_verify() + { + Test::Result result("PKCS11 ECDSA sign and verify"); + TestSession test_session(true); + + // generate key pair + PKCS11_ECDSA_KeyPair keypair = generate_ecdsa_keypair(test_session); + + std::vector plaintext(20, 0x01); + + auto sign_and_verify = [ &keypair, &plaintext, &result ](const std::string& emsa) -> void + { + Botan::PK_Signer signer(keypair.second, emsa, Botan::IEEE_1363, "pkcs11"); + auto signature = signer.sign_message(plaintext, Test::rng()); + + Botan::PK_Verifier token_verifier(keypair.first, emsa, Botan::IEEE_1363, "pkcs11"); + bool ecdsa_ok = token_verifier.verify_message(plaintext, signature); + + result.test_eq("ECDSA PKCS11 sign and verify: " + emsa, ecdsa_ok, true); + + Botan::PK_Verifier soft_verifier(keypair.first, emsa, Botan::IEEE_1363); + bool soft_ecdsa_ok = soft_verifier.verify_message(plaintext, signature); + + result.test_eq("ECDSA PKCS11 verify (in software): " + emsa, soft_ecdsa_ok, true); + }; + + sign_and_verify("Raw"); // SoftHSMv2 until now only supports "Raw" + + keypair.first.destroy(); + keypair.second.destroy(); + + return result; + } + +class PKCS11_ECDSA_Tests : public PKCS11_Test + { + public: + std::vector run() override + { + std::vector> fns = + { + test_ecdsa_privkey_import, + test_ecdsa_privkey_export, + test_ecdsa_pubkey_import, + test_ecdsa_pubkey_export, + test_ecdsa_generate_private_key, + test_ecdsa_generate_keypair, + test_ecdsa_sign_verify + }; + + return run_pkcs11_tests("PKCS11 ECDSA", fns); + } + }; + +BOTAN_REGISTER_TEST("pkcs11-ecdsa", PKCS11_ECDSA_Tests); + +/***************************** PKCS11 ECDH *****************************/ + +Test::Result test_ecdh_privkey_import() + { + Test::Result result("PKCS11 import ECDH private key"); + + TestSession test_session(true); + + // create ecdh private key + ECDH_PrivateKey priv_key(Test::rng(), EC_Group("secp256r1")); + priv_key.set_parameter_encoding(EC_Group_Encoding::EC_DOMPAR_ENC_OID); + + // import to card + EC_PrivateKeyImportProperties props(priv_key.DER_domain(), priv_key.private_value()); + props.set_token(true); + props.set_private(true); + props.set_derive(true); + + // label + std::string label = "Botan test ecdh key"; + props.set_label(label); + + PKCS11_ECDH_PrivateKey pk(test_session.session(), props); + result.test_success("ECDH private key import was successful"); + + pk.destroy(); + return result; + } + +Test::Result test_ecdh_privkey_export() + { + Test::Result result("PKCS11 export ECDH private key"); + + TestSession test_session(true); + + // create private key + ECDH_PrivateKey priv_key(Test::rng(), EC_Group("secp256r1")); + priv_key.set_parameter_encoding(EC_Group_Encoding::EC_DOMPAR_ENC_OID); + + // import to card + EC_PrivateKeyImportProperties props(priv_key.DER_domain(), priv_key.private_value()); + props.set_token(true); + props.set_private(true); + props.set_derive(true); + props.set_extractable(true); + + // label + std::string label = "Botan test ecdh key"; + props.set_label(label); + + PKCS11_ECDH_PrivateKey pk(test_session.session(), props); + + ECDH_PrivateKey exported = pk.export_key(); + result.test_success("ECDH private key export was successful"); + + pk.destroy(); + return result; + } + +Test::Result test_ecdh_pubkey_import() + { + Test::Result result("PKCS11 import ECDH public key"); + + TestSession test_session(true); + + // create ECDH private key + ECDH_PrivateKey priv_key(Test::rng(), EC_Group("secp256r1")); + priv_key.set_parameter_encoding(EC_Group_Encoding::EC_DOMPAR_ENC_OID); + + // import to card + EC_PublicKeyImportProperties props(priv_key.DER_domain(), DER_Encoder().encode(EC2OSP(priv_key.public_point(), + PointGFp::UNCOMPRESSED), OCTET_STRING).get_contents_unlocked()); + props.set_token(true); + props.set_private(false); + props.set_derive(true); + + // label + std::string label = "Botan test ECDH pub key"; + props.set_label(label); + + PKCS11_ECDH_PublicKey pk(test_session.session(), props); + result.test_success("ECDH public key import was successful"); + + pk.destroy(); + return result; + } + +Test::Result test_ecdh_pubkey_export() + { + Test::Result result("PKCS11 export ECDH public key"); + + TestSession test_session(true); + + // create public key from private key + ECDH_PrivateKey priv_key(Test::rng(), EC_Group("secp256r1")); + priv_key.set_parameter_encoding(EC_Group_Encoding::EC_DOMPAR_ENC_OID); + + // import to card + EC_PublicKeyImportProperties props(priv_key.DER_domain(), DER_Encoder().encode(EC2OSP(priv_key.public_point(), + PointGFp::UNCOMPRESSED), OCTET_STRING).get_contents_unlocked()); + props.set_token(true); + props.set_derive(true); + props.set_private(false); + + // label + std::string label = "Botan test ECDH pub key"; + props.set_label(label); + + PKCS11_ECDH_PublicKey pk(test_session.session(), props); + + ECDH_PublicKey exported = pk.export_key(); + result.test_success("ECDH public key export was successful"); + + pk.destroy(); + + return result; + } + +Test::Result test_ecdh_generate_private_key() + { + Test::Result result("PKCS11 generate ECDH private key"); + TestSession test_session(true); + + EC_PrivateKeyGenerationProperties props; + props.set_token(true); + props.set_private(true); + props.set_derive(true); + + PKCS11_ECDH_PrivateKey pk(test_session.session(), + EC_Group("secp256r1").DER_encode(EC_Group_Encoding::EC_DOMPAR_ENC_OID), props); + result.test_success("ECDH private key generation was successful"); + + pk.destroy(); + + return result; + } + +PKCS11_ECDH_KeyPair generate_ecdh_keypair(const TestSession& test_session, const std::string& label) + { + EC_PublicKeyGenerationProperties pub_props(EC_Group("secp256r1").DER_encode( + EC_Group_Encoding::EC_DOMPAR_ENC_OID)); + pub_props.set_label(label + "_PUB_KEY"); + pub_props.set_token(true); + pub_props.set_derive(true); + pub_props.set_private(false); + pub_props.set_modifiable(true); + + EC_PrivateKeyGenerationProperties priv_props; + priv_props.set_label(label + "_PRIV_KEY"); + priv_props.set_token(true); + priv_props.set_private(true); + priv_props.set_sensitive(true); + priv_props.set_extractable(false); + priv_props.set_derive(true); + priv_props.set_modifiable(true); + + return PKCS11::generate_ecdh_keypair(test_session.session(), pub_props, priv_props); + } + +Test::Result test_ecdh_generate_keypair() + { + Test::Result result("PKCS11 generate ECDH key pair"); + TestSession test_session(true); + + PKCS11_ECDH_KeyPair keypair = generate_ecdh_keypair(test_session, "Botan test ECDH key1"); + result.test_success("ECDH key pair generation was successful"); + + keypair.first.destroy(); + keypair.second.destroy(); + + return result; + } + +Test::Result test_ecdh_derive() + { + Test::Result result("PKCS11 ECDH derive"); + TestSession test_session(true); + + PKCS11_ECDH_KeyPair keypair = generate_ecdh_keypair(test_session, "Botan test ECDH key1"); + PKCS11_ECDH_KeyPair keypair2 = generate_ecdh_keypair(test_session, "Botan test ECDH key2"); + + // SoftHSMv2 only supports CKD_NULL KDF at the moment + Botan::PK_Key_Agreement ka(keypair.second, "Raw", "pkcs11"); + Botan::PK_Key_Agreement kb(keypair2.second, "Raw", "pkcs11"); + + Botan::SymmetricKey alice_key = ka.derive_key(32, unlock(EC2OSP(keypair2.first.public_point(), + PointGFp::UNCOMPRESSED))); + Botan::SymmetricKey bob_key = kb.derive_key(32, unlock(EC2OSP(keypair.first.public_point(), PointGFp::UNCOMPRESSED))); + + bool eq = alice_key == bob_key; + result.test_eq("same secret key derived", eq, true); + + keypair.first.destroy(); + keypair.second.destroy(); + keypair2.first.destroy(); + keypair2.second.destroy(); + + return result; + } + +class PKCS11_ECDH_Tests : public PKCS11_Test + { + public: + std::vector run() override + { + std::vector> fns = + { + test_ecdh_privkey_import, + test_ecdh_privkey_export, + test_ecdh_pubkey_import, + test_ecdh_pubkey_export, + test_ecdh_generate_private_key, + test_ecdh_generate_keypair, + test_ecdh_derive + }; + + return run_pkcs11_tests("PKCS11 ECDH", fns); + } + }; + +BOTAN_REGISTER_TEST("pkcs11-ecdh", PKCS11_ECDH_Tests); + +#endif + +/***************************** PKCS11 RNG *****************************/ + +Test::Result test_rng_generate_random() + { + Test::Result result("PKCS11 RNG generate random"); + TestSession test_session(true); + + PKCS11_RNG rng(test_session.session()); + + std::vector random(20); + rng.randomize(random.data(), random.size()); + result.test_ne("random data generated", random, std::vector(20)); + + return result; + } + +Test::Result test_rng_add_entropy() + { + Test::Result result("PKCS11 RNG add entropy random"); + TestSession test_session(true); + + PKCS11_RNG rng(test_session.session()); + + auto random = Test::rng().random_vec(20); + rng.add_entropy(random.data(), random.size()); + result.test_success("entropy added"); + + return result; + } + +#if defined(BOTAN_HAS_HMAC_DRBG) && defined(BOTAN_HAS_SHA2_64) + +Test::Result test_pkcs11_hmac_drbg() + { + Test::Result result("PKCS11 HMAC_DRBG using PKCS11_RNG"); + TestSession test_session(true); + + HMAC_DRBG drbg(MessageAuthenticationCode::create("HMAC(SHA-512)").release(), new PKCS11_RNG(test_session.session())); + result.test_success("HMAC_DRBG(HMAC(SHA512)) instantiated with PKCS11_RNG"); + + result.test_eq("HMAC_DRBG is not seeded yet.", drbg.is_seeded(), false); + + std::string personalization_string = "Botan PKCS#11 Tests"; + std::vector personalization_data(personalization_string.begin(), personalization_string.end()); + drbg.add_entropy(personalization_data.data(), personalization_data.size()); + + result.test_eq("HMAC_DRBG is seeded now", drbg.is_seeded(), true); + + auto rnd_vec = drbg.random_vec(256); + result.test_ne("HMAC_DRBG generated a random vector", rnd_vec, std::vector(256)); + + return result; + } +#endif + +class PKCS11_RNG_Tests : public PKCS11_Test + { + public: + std::vector run() override + { + std::vector> fns = + { + test_rng_generate_random + ,test_rng_add_entropy +#if defined(BOTAN_HAS_HMAC_DRBG )&& defined(BOTAN_HAS_SHA2_64) + ,test_pkcs11_hmac_drbg +#endif + }; + + return run_pkcs11_tests("PKCS11 RNG", fns); + } + }; + +BOTAN_REGISTER_TEST("pkcs11-rng", PKCS11_RNG_Tests); + +/***************************** PKCS11 token management *****************************/ + +Test::Result test_set_pin() + { + Test::Result result("PKCS11 set pin"); + + Module module(Test::pkcs11_lib()); + std::vector slot_vec = Slot::get_available_slots(module, true); + Slot slot(module, slot_vec.at(0)); + + PKCS11::set_pin(slot, SO_PIN_SECVEC, TEST_PIN_SECVEC); + result.test_success("PIN set with SO_PIN to TEST_PIN"); + + PKCS11::set_pin(slot, SO_PIN_SECVEC, PIN_SECVEC); + result.test_success("PIN changed back with SO_PIN"); + + return result; + } + +Test::Result test_initialize() + { + Test::Result result("PKCS11 initialize token"); + + Module module(Test::pkcs11_lib()); + std::vector slot_vec = Slot::get_available_slots(module, true); + Slot slot(module, slot_vec.at(0)); + + PKCS11::initialize_token(slot, "Botan PKCS#11 tests", SO_PIN_SECVEC, PIN_SECVEC); + result.test_success("token initialized"); + + return result; + } + +Test::Result test_change_pin() + { + Test::Result result("PKCS11 change pin"); + + Module module(Test::pkcs11_lib()); + std::vector slot_vec = Slot::get_available_slots(module, true); + Slot slot(module, slot_vec.at(0)); + + PKCS11::change_pin(slot, PIN_SECVEC, TEST_PIN_SECVEC); + result.test_success("PIN changed with PIN to TEST_PIN"); + + PKCS11::change_pin(slot, TEST_PIN_SECVEC, PIN_SECVEC); + result.test_success("PIN changed back with TEST_PIN to PIN"); + + return result; + } + +Test::Result test_change_so_pin() + { + Test::Result result("PKCS11 change so_pin"); + + Module module(Test::pkcs11_lib()); + std::vector slot_vec = Slot::get_available_slots(module, true); + Slot slot(module, slot_vec.at(0)); + + PKCS11::change_so_pin(slot, SO_PIN_SECVEC, TEST_SO_PIN_SECVEC); + result.test_success("SO_PIN changed with SO_PIN to TEST_SO_PIN"); + + PKCS11::change_so_pin(slot, TEST_SO_PIN_SECVEC, SO_PIN_SECVEC); + result.test_success("SO_PIN changed back with TEST_SO_PIN to SO_PIN"); + + return result; + } + +class PKCS11_Token_Management_Tests : public PKCS11_Test + { + public: + std::vector run() override + { + std::vector> fns = + { + test_set_pin, + test_initialize, + test_change_pin, + test_change_so_pin + }; + + return run_pkcs11_tests("PKCS11 token management", fns); + } + }; + +BOTAN_REGISTER_TEST("pkcs11-manage", PKCS11_Token_Management_Tests); + +/***************************** PKCS11 token management *****************************/ + +#if defined(BOTAN_HAS_X509_CERTIFICATES) + +Test::Result test_x509_import() + { + Test::Result result("PKCS11 X509 cert import"); + + TestSession test_session(true); + + X509_Certificate root(Test::data_file("nist_x509/test01/end.crt")); + X509_CertificateProperties props(DER_Encoder().encode(root.subject_dn()).get_contents_unlocked(), root.BER_encode()); + props.set_label("Botan PKCS#11 test certificate"); + props.set_private(false); + props.set_token(true); + + PKCS11_X509_Certificate pkcs11_cert(test_session.session(), props); + result.test_success("X509 certificate imported"); + + PKCS11_X509_Certificate pkcs11_cert2(test_session.session(), pkcs11_cert.handle()); + result.test_eq("X509 certificate by handle", pkcs11_cert == pkcs11_cert2, true); + + pkcs11_cert.destroy(); + + return result; + } + +class PKCS11_X509_Tests : public PKCS11_Test + { + public: + std::vector run() override + { + std::vector> fns = + { + test_x509_import + }; + + return run_pkcs11_tests("PKCS11 X509", fns); + } + }; + +BOTAN_REGISTER_TEST("pkcs11-x509", PKCS11_X509_Tests); + +#endif + +#endif + +} +} diff --git a/src/tests/test_pkcs11_low_level.cpp b/src/tests/test_pkcs11_low_level.cpp new file mode 100644 index 000000000..7b18f6f8b --- /dev/null +++ b/src/tests/test_pkcs11_low_level.cpp @@ -0,0 +1,852 @@ +/* +* (C) 2016 Daniel Neus +* (C) 2016 Philipp Weber +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "tests.h" +#include "test_pkcs11.h" + +#include +#include +#include +#include +#include +#include +#include + +#if defined(BOTAN_HAS_PKCS11) + #include +#endif + +#if defined(BOTAN_HAS_DYNAMIC_LOADER) + #include +#endif + +namespace Botan_Tests { + +namespace { + +#if defined(BOTAN_HAS_PKCS11) +#if defined(BOTAN_HAS_DYNAMIC_LOADER) + +using namespace Botan; +using namespace PKCS11; + +class RAII_LowLevel + { + public: + RAII_LowLevel() : m_module(Test::pkcs11_lib()), m_func_list(nullptr), m_low_level(), m_session_handle(0), + m_is_session_open(false), m_is_logged_in(false) + { + LowLevel::C_GetFunctionList(m_module, &m_func_list); + m_low_level.reset(new LowLevel(m_func_list)); + + C_InitializeArgs init_args = { nullptr, nullptr, nullptr, nullptr, static_cast(Flag::OsLockingOk), nullptr }; + + m_low_level->C_Initialize(&init_args); + } + ~RAII_LowLevel() BOTAN_NOEXCEPT + { + + if(m_is_session_open) + { + if(m_is_logged_in) + { + m_low_level.get()->C_Logout(m_session_handle, nullptr); + } + + m_low_level.get()->C_CloseSession(m_session_handle, nullptr); + } + + m_low_level.get()->C_Finalize(nullptr, nullptr); + } + + std::vector get_slots(bool token_present) const + { + std::vector slots; + m_low_level.get()->C_GetSlotList(token_present, slots); + + if(slots.empty()) + { + throw Exception("No slot with attached token found"); + } + + return slots; + } + + inline SessionHandle open_session(Flags session_flags) + { + std::vector slots = get_slots(true); + m_low_level.get()->C_OpenSession(slots.at(0), session_flags, nullptr, nullptr, &m_session_handle); + m_is_session_open = true; + return m_session_handle; + } + + inline SessionHandle open_rw_session_with_user_login() + { + Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession); + SessionHandle handle = open_session(session_flags); + login(UserType::User, PIN_SECVEC); + return handle; + } + + inline SessionHandle get_session_handle() const + { + if(!m_is_session_open) + { + throw Exception("no open session"); + } + return m_session_handle; + } + + inline void close_session() + { + if(!m_is_session_open) + { + throw Exception("no open session"); + } + + m_low_level.get()->C_CloseSession(m_session_handle); + m_is_session_open = false; + } + + inline void login(UserType user_type, const secure_vector& pin) + { + if(!m_is_session_open) + { + throw Exception("no open session"); + } + + if(m_is_logged_in) + { + throw Exception("Already logged in"); + } + + m_low_level.get()->C_Login(m_session_handle, user_type, pin); + m_is_logged_in = true; + } + + inline void logout() + { + if(!m_is_logged_in) + { + throw Exception("Not logged in"); + } + + m_low_level.get()->C_Logout(m_session_handle); + m_is_logged_in = false; + } + + LowLevel* get() const + { + return m_low_level.get(); + } + private: + Dynamically_Loaded_Library m_module; + FunctionListPtr m_func_list; + std::unique_ptr m_low_level; + SessionHandle m_session_handle; + bool m_is_session_open; + bool m_is_logged_in; + }; + +bool no_op(ReturnValue*) + { + return true; + } + +using PKCS11_BoundTestFunction = std::function; + +// tests all 3 variants +Test::Result test_function(const std::string& name, const PKCS11_BoundTestFunction& test_func, + const std::string& revert_fn_name, const PKCS11_BoundTestFunction& revert_func, + bool expect_failure, ReturnValue expected_return_value) + { + std::string test_name = revert_fn_name.empty() ? "PKCS 11 low level - " + name : "PKCS 11 low level - " + name + "/" + + revert_fn_name; + Test::Result result(test_name); + + // test throw variant + if(expect_failure) + { + result.test_throws(name + " fails as expected", [ test_func ]() + { + test_func(ThrowException); + }); + } + else + { + test_func(ThrowException); + result.test_success(name + " did not throw and completed successfully"); + + if(!revert_fn_name.empty()) + { + revert_func(ThrowException); + result.test_success(revert_fn_name + " did not throw and completed successfully"); + } + } + + // test bool return variant + bool success = test_func(nullptr); + result.test_eq(name, success, !expect_failure); + if(success && !revert_fn_name.empty()) + { + success = revert_func(nullptr); + result.test_eq(revert_fn_name, success, !expect_failure); + } + + // test ReturnValue variant + ReturnValue rv; + success = test_func(&rv); + result.test_eq(name, success, !expect_failure); + if(!expect_failure) + { + result.test_rc_ok(name, static_cast< uint32_t >(rv)); + } + else + { + result.test_rc_fail(name, "return value should be: " + std::to_string(static_cast< uint32_t >(expected_return_value)), + static_cast< uint32_t >(rv)); + } + + if(success && !revert_fn_name.empty()) + { + success = revert_func(&rv); + result.test_eq(revert_fn_name, success, !expect_failure); + result.test_rc_ok(revert_fn_name, static_cast< uint32_t >(rv)); + } + + return result; + } + +Test::Result test_function(const std::string& name, const PKCS11_BoundTestFunction& test_func, + bool expect_failure, ReturnValue expected_return_value) + { + return test_function(name, test_func, std::string(), no_op, expect_failure, expected_return_value); + } + +Test::Result test_function(const std::string& name, const PKCS11_BoundTestFunction& test_func) + { + return test_function(name, test_func, std::string(), no_op, false, ReturnValue::OK); + } + +Test::Result test_function(const std::string& name, const PKCS11_BoundTestFunction& test_func, + const std::string& revert_fn_name, const PKCS11_BoundTestFunction& revert_func) + { + return test_function(name, test_func, revert_fn_name, revert_func, false, ReturnValue::OK); + } + +Test::Result test_low_level_ctor() + { + Test::Result result("PKCS 11 low level - LowLevel ctor"); + + Dynamically_Loaded_Library pkcs11_module(Test::pkcs11_lib()); + FunctionListPtr func_list(nullptr); + LowLevel::C_GetFunctionList(pkcs11_module, &func_list); + + LowLevel p11_low_level(func_list); + result.test_success("LowLevel ctor does complete for valid function list"); + + result.test_throws("LowLevel ctor fails for invalid function list pointer", []() + { + LowLevel p11_low_level2(nullptr); + }); + + return result; + } + +Test::Result test_c_get_function_list() + { + Dynamically_Loaded_Library pkcs11_module(Test::pkcs11_lib()); + FunctionListPtr func_list = nullptr; + return test_function("C_GetFunctionList", std::bind(&LowLevel::C_GetFunctionList, std::ref(pkcs11_module), &func_list, + std::placeholders::_1)); + } + +Test::Result test_initialize_finalize() + { + Dynamically_Loaded_Library pkcs11_module(Test::pkcs11_lib()); + FunctionListPtr func_list = nullptr; + LowLevel::C_GetFunctionList(pkcs11_module, &func_list); + + LowLevel p11_low_level(func_list); + + // setting Flag::OsLockingOk should be the normal use case + C_InitializeArgs init_args = { nullptr, nullptr, nullptr, nullptr, static_cast(Flag::OsLockingOk), nullptr }; + + auto init_bind = std::bind(&LowLevel::C_Initialize, p11_low_level, &init_args, std::placeholders::_1); + auto finalize_bind = std::bind(&LowLevel::C_Finalize, p11_low_level, nullptr, std::placeholders::_1); + return test_function("C_Initialize", init_bind, "C_Finalize", finalize_bind); + } + +Test::Result test_c_get_info() + { + RAII_LowLevel p11_low_level; + + Info info = {}; + Test::Result result = test_function("C_GetInfo", std::bind(&LowLevel::C_GetInfo, *p11_low_level.get(), &info, + std::placeholders::_1)); + result.test_ne("C_GetInfo crypto major version", info.cryptokiVersion.major, 0); + + return result; + } + +Test::Result test_c_get_slot_list() + { + RAII_LowLevel p11_low_level; + + std::vector slot_vec; + + // assumes at least one smartcard reader is attached + bool token_present = false; + + auto binder = std::bind(static_cast< bool (LowLevel::*)(bool, std::vector&, ReturnValue*) const> + (&LowLevel::C_GetSlotList), *p11_low_level.get(), std::ref(token_present), std::ref(slot_vec), std::placeholders::_1); + + Test::Result result = test_function("C_GetSlotList", binder); + result.test_ne("C_GetSlotList number of slots without attached token > 0", slot_vec.size(), 0); + + // assumes at least one smartcard reader with connected smartcard is attached + slot_vec.clear(); + token_present = true; + result.merge(test_function("C_GetSlotList", binder)); + result.test_ne("C_GetSlotList number of slots with attached token > 0", slot_vec.size(), 0); + + return result; + } + +Test::Result test_c_get_slot_info() + { + RAII_LowLevel p11_low_level; + std::vector slot_vec = p11_low_level.get_slots(false); + + SlotInfo slot_info = {}; + Test::Result result = test_function("C_GetSlotInfo", std::bind(&LowLevel::C_GetSlotInfo, *p11_low_level.get(), + slot_vec.at(0), &slot_info, std::placeholders::_1)); + + std::string slot_desc(reinterpret_cast< char* >(slot_info.slotDescription)); + result.test_ne("C_GetSlotInfo returns non empty description", slot_desc.size(), 0); + + return result; + } + +Test::Result test_c_get_token_info() + { + RAII_LowLevel p11_low_level; + std::vector slot_vec = p11_low_level.get_slots(true); + + TokenInfo token_info = {}; + Test::Result result = test_function("C_GetTokenInfo", std::bind(&LowLevel::C_GetTokenInfo, *p11_low_level.get(), + slot_vec.at(0), &token_info, std::placeholders::_1)); + + std::string serial(reinterpret_cast< char* >(token_info.serialNumber)); + result.test_ne("C_GetTokenInfo returns non empty serial number", serial.size(), 0); + + return result; + } + +Test::Result test_c_wait_for_slot_event() + { + RAII_LowLevel p11_low_level; + + Flags flags = PKCS11::flags(Flag::DontBlock); + SlotId slot_id = 0; + + return test_function("C_WaitForSlotEvent", std::bind(&LowLevel::C_WaitForSlotEvent, *p11_low_level.get(), + flags, &slot_id, nullptr, std::placeholders::_1), true, ReturnValue::NoEvent); + } + +Test::Result test_c_get_mechanism_list() + { + RAII_LowLevel p11_low_level; + std::vector slot_vec = p11_low_level.get_slots(true); + + std::vector mechanisms; + + auto binder = std::bind(static_cast< bool (LowLevel::*)(SlotId, std::vector&, ReturnValue*) const> + (&LowLevel::C_GetMechanismList), *p11_low_level.get(), slot_vec.at(0), std::ref(mechanisms), std::placeholders::_1); + + Test::Result result = test_function("C_GetMechanismList", binder); + result.confirm("C_GetMechanismList returns non empty mechanisms list", !mechanisms.empty()); + + return result; + } + +Test::Result test_c_get_mechanism_info() + { + RAII_LowLevel p11_low_level; + std::vector slot_vec = p11_low_level.get_slots(true); + + std::vector mechanisms; + p11_low_level.get()->C_GetMechanismList(slot_vec.at(0), mechanisms); + + MechanismInfo mechanism_info = {}; + return test_function("C_GetMechanismInfo", std::bind(&LowLevel::C_GetMechanismInfo, *p11_low_level.get(), + slot_vec.at(0), mechanisms.at(0), &mechanism_info, std::placeholders::_1)); + } + +Test::Result test_c_init_token() + { + RAII_LowLevel p11_low_level; + std::vector slot_vec = p11_low_level.get_slots(true); + + const std::string label = "Botan PKCS#11 tests"; + + auto sec_vec_binder = std::bind( + static_cast< bool (LowLevel::*)(SlotId, const secure_vector&, const std::string&, ReturnValue*) const> + (&LowLevel::C_InitToken>), *p11_low_level.get(), slot_vec.at(0), std::ref(SO_PIN_SECVEC), + std::ref(label), std::placeholders::_1); + + return test_function("C_InitToken", sec_vec_binder); + } + +Test::Result test_open_close_session() + { + RAII_LowLevel p11_low_level; + std::vector slot_vec = p11_low_level.get_slots(true); + + // public read only session + Flags flags = PKCS11::flags(Flag::SerialSession); + SessionHandle session_handle = 0; + + auto open_session_bind = std::bind(&LowLevel::C_OpenSession, *p11_low_level.get(), + slot_vec.at(0), std::ref(flags), nullptr, nullptr, &session_handle, std::placeholders::_1); + + auto close_session_bind = std::bind(&LowLevel::C_CloseSession, *p11_low_level.get(), + std::ref(session_handle), std::placeholders::_1); + + Test::Result result = test_function("C_OpenSession", open_session_bind, "C_CloseSession", close_session_bind); + + // public read write session + flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession); + result.merge(test_function("C_OpenSession", open_session_bind, "C_CloseSession", close_session_bind)); + + return result; + } + +Test::Result test_c_close_all_sessions() + { + RAII_LowLevel p11_low_level; + std::vector slot_vec = p11_low_level.get_slots(true); + + auto open_two_sessions = [ &slot_vec, &p11_low_level ]() -> void + { + // public read only session + Flags flags = PKCS11::flags(Flag::SerialSession); + SessionHandle first_session_handle = 0, second_session_handle = 0; + + p11_low_level.get()->C_OpenSession(slot_vec.at(0), flags, nullptr, nullptr, &first_session_handle); + + flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession); + p11_low_level.get()->C_OpenSession(slot_vec.at(0), flags, nullptr, nullptr, &second_session_handle); + }; + + open_two_sessions(); + + Test::Result result("PKCS 11 low level - C_CloseAllSessions"); + + // test throw variant + p11_low_level.get()->C_CloseAllSessions(slot_vec.at(0)); + result.test_success("C_CloseAllSessions does not throw"); + + // test bool return variant + open_two_sessions(); + + bool success = p11_low_level.get()->C_CloseAllSessions(slot_vec.at(0), nullptr); + result.test_eq("C_CloseAllSessions", success, true); + + // test ReturnValue variant + open_two_sessions(); + + ReturnValue rv = static_cast< ReturnValue >(-1); + success = p11_low_level.get()->C_CloseAllSessions(slot_vec.at(0), &rv); + result.test_eq("C_CloseAllSessions", success, true); + result.test_rc_ok("C_CloseAllSessions", static_cast< uint32_t >(rv)); + + return result; + } + +Test::Result test_c_get_session_info() + { + RAII_LowLevel p11_low_level; + std::vector slot_vec = p11_low_level.get_slots(true); + + // public read only session + Flags flags = PKCS11::flags(Flag::SerialSession); + SessionHandle session_handle = p11_low_level.open_session(flags); + + SessionInfo session_info = {}; + Test::Result result = test_function("C_GetSessionInfo", std::bind(&LowLevel::C_GetSessionInfo, *p11_low_level.get(), + session_handle, &session_info, std::placeholders::_1)); + + result.confirm("C_GetSessionInfo returns same slot id as during call to C_OpenSession", + session_info.slotID == slot_vec.at(0)); + result.confirm("C_GetSessionInfo returns same flags as during call to C_OpenSession", session_info.flags == flags); + result.confirm("C_GetSessionInfo returns public read only session state", + session_info.state == static_cast(SessionState::RoPublicSession)); + + return result; + } + +Test::Result login_logout_helper(const RAII_LowLevel& p11_low_level, SessionHandle handle, UserType user_type, + const std::string& pin) + { + secure_vector pin_as_sec_vec(pin.begin(), pin.end()); + + auto login_secvec_binder = std::bind(static_cast< bool (LowLevel::*)(SessionHandle, UserType, + const secure_vector&, ReturnValue*) const> + (&LowLevel::C_Login>), *p11_low_level.get(), + handle, user_type, std::ref(pin_as_sec_vec), std::placeholders::_1); + + auto logout_binder = std::bind(static_cast< bool (LowLevel::*)(SessionHandle, ReturnValue*) const> + (&LowLevel::C_Logout), *p11_low_level.get(), handle, std::placeholders::_1); + + return test_function("C_Login", login_secvec_binder, "C_Logout", logout_binder); + } + +Test::Result test_c_login_logout_security_officier() + { + RAII_LowLevel p11_low_level; + + // can only login to R/W session + Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession); + SessionHandle session_handle = p11_low_level.open_session(session_flags); + + return login_logout_helper(p11_low_level, session_handle, UserType::SO, SO_PIN); + } + +Test::Result test_c_login_logout_user() + { + RAII_LowLevel p11_low_level; + + // R/O session + Flags session_flags = PKCS11::flags(Flag::SerialSession); + SessionHandle session_handle = p11_low_level.open_session(session_flags); + Test::Result result = login_logout_helper(p11_low_level, session_handle, UserType::User, PIN); + p11_low_level.close_session(); + + // R/W session + session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession); + session_handle = p11_low_level.open_session(session_flags); + + result.merge(login_logout_helper(p11_low_level, session_handle, UserType::User, PIN)); + + return result; + } + +Test::Result test_c_init_pin() + { + RAII_LowLevel p11_low_level; + + // C_InitPIN can only be called in the "R/W SO Functions" state + Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession); + SessionHandle session_handle = p11_low_level.open_session(session_flags); + + p11_low_level.login(UserType::SO, SO_PIN_SECVEC); + + auto sec_vec_binder = std::bind( + static_cast< bool (LowLevel::*)(SessionHandle, const secure_vector&, ReturnValue*) const> + (&LowLevel::C_InitPIN>), *p11_low_level.get(), session_handle, std::ref(PIN_SECVEC), + std::placeholders::_1); + + return test_function("C_InitPIN", sec_vec_binder); + } + +Test::Result test_c_set_pin() + { + RAII_LowLevel p11_low_level; + + // C_SetPIN can only be called in the "R / W Public Session" state, "R / W SO Functions" state, or "R / W User Functions" state + Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession); + SessionHandle session_handle = p11_low_level.open_session(session_flags); + + // now we are in "R / W Public Session" state: this will change the pin of the user + + auto get_pin_bind = [ &session_handle, &p11_low_level ](const secure_vector& old_pin, + const secure_vector& new_pin) -> PKCS11_BoundTestFunction + { + return std::bind(static_cast< bool (LowLevel::*)(SessionHandle, const secure_vector&, + const secure_vector&, ReturnValue*) const> + (&LowLevel::C_SetPIN>), *p11_low_level.get(), session_handle, + old_pin, new_pin, std::placeholders::_1); + }; + + const std::string test_pin("654321"); + const auto test_pin_secvec = secure_vector(test_pin.begin(), test_pin.end()); + + PKCS11_BoundTestFunction set_pin_bind = get_pin_bind(PIN_SECVEC, test_pin_secvec); + PKCS11_BoundTestFunction revert_pin_bind = get_pin_bind(test_pin_secvec, PIN_SECVEC); + + Test::Result result = test_function("C_SetPIN", set_pin_bind, "C_SetPIN", revert_pin_bind); + + // change pin in "R / W User Functions" state + p11_low_level.login(UserType::User, PIN_SECVEC); + + result.merge(test_function("C_SetPIN", set_pin_bind, "C_SetPIN", revert_pin_bind)); + p11_low_level.logout(); + + // change so_pin in "R / W SO Functions" state + const std::string test_so_pin = "87654321"; + secure_vector test_so_pin_secvec(test_so_pin.begin(), test_so_pin.end()); + p11_low_level.login(UserType::SO, SO_PIN_SECVEC); + + PKCS11_BoundTestFunction set_so_pin_bind = get_pin_bind(SO_PIN_SECVEC, test_so_pin_secvec); + PKCS11_BoundTestFunction revert_so_pin_bind = get_pin_bind(test_so_pin_secvec, SO_PIN_SECVEC); + + result.merge(test_function("C_SetPIN", set_so_pin_bind, "C_SetPIN", revert_so_pin_bind)); + + return result; + } + +// Simple data object +ObjectClass object_class = ObjectClass::Data; +std::string label = "A data object"; +std::string data = "Sample data"; +Bbool btrue = True; + +std::array dtemplate = + { + { + { static_cast(AttributeType::Class), &object_class, sizeof(object_class) }, + { static_cast(AttributeType::Token), &btrue, sizeof(btrue) }, + { static_cast(AttributeType::Label), const_cast< char* >(label.c_str()), label.size() }, + { static_cast(AttributeType::Value), const_cast< char* >(data.c_str()), data.size() } + } + }; + +ObjectHandle create_simple_data_object(const RAII_LowLevel& p11_low_level) + { + ObjectHandle object_handle; + p11_low_level.get()->C_CreateObject(p11_low_level.get_session_handle(), dtemplate.data(), dtemplate.size(), + &object_handle); + return object_handle; + } + +Test::Result test_c_create_object_c_destroy_object() + { + RAII_LowLevel p11_low_level; + SessionHandle session_handle = p11_low_level.open_rw_session_with_user_login(); + + ObjectHandle object_handle(0); + + auto create_bind = std::bind(&LowLevel::C_CreateObject, *p11_low_level.get(), + session_handle, dtemplate.data(), dtemplate.size(), &object_handle, std::placeholders::_1); + + auto destroy_bind = std::bind(&LowLevel::C_DestroyObject, *p11_low_level.get(), session_handle, + std::ref(object_handle), std::placeholders::_1); + + return test_function("C_CreateObject", create_bind, "C_DestroyObject", destroy_bind); + } + +Test::Result test_c_get_object_size() + { + RAII_LowLevel p11_low_level; + + Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession); + SessionHandle session_handle = p11_low_level.open_session(session_flags); + + p11_low_level.login(UserType::User, PIN_SECVEC); + + ObjectHandle object_handle = create_simple_data_object(p11_low_level); + Ulong object_size = 0; + + auto bind = std::bind(&LowLevel::C_GetObjectSize, *p11_low_level.get(), + session_handle, object_handle, &object_size, std::placeholders::_1); + + Test::Result result = test_function("C_GetObjectSize", bind); + result.test_ne("Object size", object_size, 0); + + // cleanup + p11_low_level.get()->C_DestroyObject(session_handle, object_handle); + + return result; + } + +Test::Result test_c_get_attribute_value() + { + RAII_LowLevel p11_low_level; + SessionHandle session_handle = p11_low_level.open_rw_session_with_user_login(); + + ObjectHandle object_handle = create_simple_data_object(p11_low_level); + + std::map < AttributeType, secure_vector> getter = + { + { AttributeType::Label, secure_vector() }, + { AttributeType::Value, secure_vector() } + }; + + auto bind = std::bind(static_cast< bool (LowLevel::*)(SessionHandle, ObjectHandle, + std::map>&, ReturnValue*) const> + (&LowLevel::C_GetAttributeValue>), *p11_low_level.get(), session_handle, + object_handle, std::ref(getter), std::placeholders::_1); + + Test::Result result = test_function("C_GetAttributeValue", bind); + + std::string _label(getter[ AttributeType::Label ].begin(), getter[ AttributeType::Label ].end()); + std::string value(getter[ AttributeType::Value ].begin(), getter[ AttributeType::Value ].end()); + result.test_eq("label", _label, "A data object"); + result.test_eq("value", value, "Sample data"); + + // cleanup + p11_low_level.get()->C_DestroyObject(session_handle, object_handle); + + return result; + } + +std::map < AttributeType, std::vector> get_attribute_values(const RAII_LowLevel& p11_low_level, + SessionHandle session_handle, + ObjectHandle object_handle, const std::vector& attribute_types) + { + std::map < AttributeType, std::vector> received_attributes; + + for(const auto& type : attribute_types) + { + received_attributes.emplace(type, std::vector()); + } + + p11_low_level.get()->C_GetAttributeValue(session_handle, object_handle, received_attributes); + + return received_attributes; + } + +Test::Result test_c_set_attribute_value() + { + RAII_LowLevel p11_low_level; + + Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession); + SessionHandle session_handle = p11_low_level.open_session(session_flags); + + p11_low_level.login(UserType::User, PIN_SECVEC); + + ObjectHandle object_handle = create_simple_data_object(p11_low_level); + + std::string new_label = "A modified data object"; + + std::map < AttributeType, secure_vector> new_attributes = + { + { AttributeType::Label, secure_vector(new_label.begin(), new_label.end()) } + }; + + auto bind = std::bind(static_cast< bool (LowLevel::*)(SessionHandle, ObjectHandle, + std::map>&, ReturnValue*) const> + (&LowLevel::C_SetAttributeValue>), *p11_low_level.get(), session_handle, + object_handle, std::ref(new_attributes), std::placeholders::_1); + + Test::Result result = test_function("C_SetAttributeValue", bind); + + // get attributes and check if they are changed correctly + std::vector types = { AttributeType::Label, AttributeType::Value }; + auto received_attributes = get_attribute_values(p11_low_level, session_handle, object_handle, types); + + std::string retrieved_label(received_attributes[ AttributeType::Label ].begin(), + received_attributes[ AttributeType::Label ].end()); + + result.test_eq("label", new_label, retrieved_label); + + // cleanup + p11_low_level.get()->C_DestroyObject(session_handle, object_handle); + + return result; + } + +Test::Result test_c_copy_object() + { + RAII_LowLevel p11_low_level; + SessionHandle session_handle = p11_low_level.open_rw_session_with_user_login(); + + ObjectHandle object_handle = create_simple_data_object(p11_low_level); + ObjectHandle copied_object_handle = 0; + + std::string copied_label = "A copied data object"; + + Attribute copy_attribute_values = { static_cast< CK_ATTRIBUTE_TYPE >(AttributeType::Label), const_cast< char* >(copied_label.c_str()), copied_label.size() }; + + auto binder = std::bind(&LowLevel::C_CopyObject, *p11_low_level.get(), session_handle, object_handle, + ©_attribute_values, 1, &copied_object_handle, std::placeholders::_1); + + Test::Result result = test_function("C_CopyObject", binder); + + // get attributes and check if its copied correctly + std::vector types = { AttributeType::Label, AttributeType::Value }; + auto received_attributes = get_attribute_values(p11_low_level, session_handle, copied_object_handle, types); + + std::string retrieved_label(received_attributes[ AttributeType::Label ].begin(), + received_attributes[ AttributeType::Label ].end()); + + result.test_eq("label", copied_label, retrieved_label); + + // cleanup + p11_low_level.get()->C_DestroyObject(session_handle, object_handle); + p11_low_level.get()->C_DestroyObject(session_handle, copied_object_handle); + + return result; + } + +class LowLevelTests : public Test + { + public: + std::vector run() override + { + std::vector results; + + std::vector> fns = + { + test_c_get_function_list + ,test_low_level_ctor + ,test_initialize_finalize + ,test_c_get_info + ,test_c_get_slot_list + ,test_c_get_slot_info + ,test_c_get_token_info + ,test_c_wait_for_slot_event + ,test_c_get_mechanism_list + ,test_c_get_mechanism_info + ,test_open_close_session + ,test_c_close_all_sessions + ,test_c_get_session_info + ,test_c_init_token + ,test_c_login_logout_security_officier /* only possible if token is initialized */ + ,test_c_init_pin + ,test_c_login_logout_user /* only possible if token is initialized and user pin is set */ + ,test_c_set_pin + ,test_c_create_object_c_destroy_object + ,test_c_get_object_size + ,test_c_get_attribute_value + ,test_c_set_attribute_value + ,test_c_copy_object + }; + + for(size_t i = 0; i != fns.size(); ++i) + { + try + { + results.push_back(fns[ i ]()); + } + catch(PKCS11_ReturnError& e) + { + results.push_back(Test::Result::Failure("PKCS11 low level test " + std::to_string(i), e.what())); + + if(e.get_return_value() == ReturnValue::PinIncorrect) + { + break; // Do not continue to not potentially lock the token + } + } + catch(std::exception& e) + { + results.push_back(Test::Result::Failure("PKCS11 low level test " + std::to_string(i), e.what())); + } + } + + return results; + } + }; + +BOTAN_REGISTER_TEST("pkcs11-lowlevel", LowLevelTests); + +#endif +#endif + +} +} diff --git a/src/tests/tests.cpp b/src/tests/tests.cpp index a79e05aba..aae4174b9 100644 --- a/src/tests/tests.cpp +++ b/src/tests/tests.cpp @@ -254,34 +254,6 @@ bool Test::Result::test_eq(const std::string& what, bool produced, bool expected return test_is_eq(what, produced, expected); } -bool Test::Result::test_rc_ok(const std::string& what, int rc) - { - if(rc != 0) - { - std::ostringstream err; - err << m_who; - err << " " << what; - err << " unexpectedly failed with error code " << rc; - return test_failure(err.str()); - } - - return test_success(); - } - -bool Test::Result::test_rc_fail(const std::string& func, const std::string& why, int rc) - { - if(rc == 0) - { - std::ostringstream err; - err << m_who; - err << " call to " << func << " unexpectedly succeeded"; - err << " expecting failure because " << why; - return test_failure(err.str()); - } - - return test_success(); - } - bool Test::Result::test_rc(const std::string& func, int expected, int rc) { if(expected != rc) @@ -436,17 +408,20 @@ Botan::RandomNumberGenerator* Test::m_test_rng = nullptr; std::string Test::m_data_dir; size_t Test::m_soak_level = 0; bool Test::m_log_success = false; +std::string Test::m_pkcs11_lib; //static void Test::setup_tests(size_t soak, bool log_success, const std::string& data_dir, + const std::string& pkcs11_lib, Botan::RandomNumberGenerator* rng) { m_data_dir = data_dir; m_soak_level = soak; m_log_success = log_success; m_test_rng = rng; + m_pkcs11_lib = pkcs11_lib; } //static @@ -473,6 +448,12 @@ bool Test::log_success() return m_log_success; } +//static +std::string Test::pkcs11_lib() + { + return m_pkcs11_lib; + } + //static Botan::RandomNumberGenerator& Test::rng() { diff --git a/src/tests/tests.h b/src/tests/tests.h index c7bed64d9..b52ecce0d 100644 --- a/src/tests/tests.h +++ b/src/tests/tests.h @@ -184,8 +184,40 @@ class Test bool test_lt(const std::string& what, size_t produced, size_t expected); bool test_gte(const std::string& what, size_t produced, size_t expected); - bool test_rc_ok(const std::string& func, int rc); - bool test_rc_fail(const std::string& func, const std::string& why, int rc); + template + bool test_rc_ok(const std::string& func, T rc) + { + static_assert(std::is_integral::value, "Integer required."); + + if(rc != 0) + { + std::ostringstream err; + err << m_who; + err << " " << func; + err << " unexpectedly failed with error code " << rc; + return test_failure(err.str()); + } + + return test_success(); + } + + template + bool test_rc_fail(const std::string& func, const std::string& why, T rc) + { + static_assert(std::is_integral::value, "Integer required."); + + if(rc == 0) + { + std::ostringstream err; + err << m_who; + err << " call to " << func << " unexpectedly succeeded"; + err << " expecting failure because " << why; + return test_failure(err.str()); + } + + return test_success(); + } + bool test_rc(const std::string& func, int expected, int rc); #if defined(BOTAN_HAS_BIGINT) @@ -311,10 +343,12 @@ class Test static void setup_tests(size_t soak, bool log_succcss, const std::string& data_dir, + const std::string& pkcs11_lib, Botan::RandomNumberGenerator* rng); static size_t soak_level(); static bool log_success(); + static std::string pkcs11_lib(); static const std::string& data_dir(); @@ -327,6 +361,7 @@ class Test static Botan::RandomNumberGenerator* m_test_rng; static size_t m_soak_level; static bool m_log_success; + static std::string m_pkcs11_lib; }; /* -- cgit v1.2.3 From d4f3e7c4ac584daa9d7e1ae10cb3412e450e25cf Mon Sep 17 00:00:00 2001 From: Matthias Gierlings Date: Fri, 29 Apr 2016 20:44:30 +0200 Subject: Reduction of code complexity in TLS classes. -reduced number of parameters in various methods -reduced cyclomatic complexity (McCabe-Metric) -removed "TLSEXT_HEARTBEAT_SUPPORT" from tls_extensions.h (leftover from heartbeat extension removal?) --- src/cli/tls_client.cpp | 17 +- src/cli/tls_proxy.cpp | 1 + src/cli/tls_server.cpp | 10 +- src/lib/cert/x509/x509_ca.cpp | 19 +- src/lib/cert/x509/x509_ca.h | 24 +- src/lib/cert/x509/x509self.cpp | 11 +- src/lib/tls/info.txt | 1 + src/lib/tls/msg_cert_req.cpp | 6 +- src/lib/tls/msg_certificate.cpp | 6 +- src/lib/tls/msg_client_hello.cpp | 31 +- src/lib/tls/msg_server_hello.cpp | 29 +- src/lib/tls/tls_blocking.cpp | 19 +- src/lib/tls/tls_blocking.h | 5 +- src/lib/tls/tls_channel.cpp | 229 ++++----- src/lib/tls/tls_channel.h | 81 ++- src/lib/tls/tls_ciphersuite.h | 37 +- src/lib/tls/tls_client.cpp | 87 ++-- src/lib/tls/tls_client.h | 95 ++-- src/lib/tls/tls_extensions.h | 2 +- src/lib/tls/tls_handshake_msg.h | 14 + src/lib/tls/tls_messages.h | 83 +++- src/lib/tls/tls_record.cpp | 151 +++--- src/lib/tls/tls_record.h | 91 +++- src/lib/tls/tls_server.cpp | 822 ++++++++++++++++--------------- src/lib/tls/tls_server.h | 52 +- src/lib/tls/tls_server_handshake_state.h | 47 ++ src/lib/tls/tls_session.cpp | 58 +-- src/lib/tls/tls_session.h | 118 ++++- src/tests/unit_tls.cpp | 52 +- 29 files changed, 1256 insertions(+), 942 deletions(-) create mode 100644 src/lib/tls/tls_server_handshake_state.h (limited to 'src/lib/cert') diff --git a/src/cli/tls_client.cpp b/src/cli/tls_client.cpp index 6af2f56f8..7db8bacf8 100644 --- a/src/cli/tls_client.cpp +++ b/src/cli/tls_client.cpp @@ -1,5 +1,6 @@ /* * (C) 2014,2015 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -118,17 +119,19 @@ class TLS_Client final : public Command version = Botan::TLS::Protocol_Version::TLS_V11; } - Botan::TLS::Client client(socket_write, - std::bind(&TLS_Client::process_data, this, _1, _2), - std::bind(&TLS_Client::alert_received, this, _1, _2, _3), - std::bind(&TLS_Client::handshake_complete, this, _1), + Botan::TLS::Client client(Botan::TLS::Client::Callbacks( + socket_write, + std::bind(&TLS_Client::process_data, this, _1, _2), + std::bind(&TLS_Client::alert_received, this, _1, _2, _3), + std::bind(&TLS_Client::handshake_complete, this, _1)), *session_mgr, creds, *policy, rng(), - Botan::TLS::Server_Information(host, port), - version, - protocols_to_offer); + Botan::TLS::Client::Properties( + Botan::TLS::Server_Information(host, port), + version, + protocols_to_offer)); bool first_active = true; diff --git a/src/cli/tls_proxy.cpp b/src/cli/tls_proxy.cpp index 2929e473d..f5f666cf1 100644 --- a/src/cli/tls_proxy.cpp +++ b/src/cli/tls_proxy.cpp @@ -1,6 +1,7 @@ /* * TLS Server Proxy * (C) 2014,2015 Jack Lloyd +* (C) 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ diff --git a/src/cli/tls_server.cpp b/src/cli/tls_server.cpp index 2496f5508..335f36c97 100644 --- a/src/cli/tls_server.cpp +++ b/src/cli/tls_server.cpp @@ -1,6 +1,7 @@ /* * TLS echo server using BSD sockets * (C) 2014 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -106,10 +107,11 @@ class TLS_Server final : public Command } }; - Botan::TLS::Server server(socket_write, - proc_fn, - std::bind(&TLS_Server::alert_received, this, _1, _2, _3), - std::bind(&TLS_Server::handshake_complete, this, _1), + Botan::TLS::Server server(Botan::TLS::Server::Callbacks( + socket_write, + proc_fn, + std::bind(&TLS_Server::alert_received, this, _1, _2, _3), + std::bind(&TLS_Server::handshake_complete, this, _1)), session_manager, creds, policy, diff --git a/src/lib/cert/x509/x509_ca.cpp b/src/lib/cert/x509/x509_ca.cpp index 147fdd6ad..3f7af77f5 100644 --- a/src/lib/cert/x509/x509_ca.cpp +++ b/src/lib/cert/x509/x509_ca.cpp @@ -78,8 +78,10 @@ X509_Certificate X509_CA::sign_request(const PKCS10_Request& req, return make_cert(m_signer, rng, m_ca_sig_algo, req.raw_public_key(), - not_before, not_after, - m_cert.subject_dn(), req.subject_dn(), + Certificate_Properties(not_before, + not_after, + m_cert.subject_dn(), + req.subject_dn()), extensions); } @@ -90,10 +92,7 @@ X509_Certificate X509_CA::make_cert(PK_Signer* signer, RandomNumberGenerator& rng, const AlgorithmIdentifier& sig_algo, const std::vector& pub_key, - const X509_Time& not_before, - const X509_Time& not_after, - const X509_DN& issuer_dn, - const X509_DN& subject_dn, + const Certificate_Properties properties, const Extensions& extensions) { const size_t X509_CERT_VERSION = 3; @@ -112,14 +111,14 @@ X509_Certificate X509_CA::make_cert(PK_Signer* signer, .encode(serial_no) .encode(sig_algo) - .encode(issuer_dn) + .encode(properties.get_issuer_dn()) .start_cons(SEQUENCE) - .encode(not_before) - .encode(not_after) + .encode(properties.get_not_before()) + .encode(properties.get_not_after()) .end_cons() - .encode(subject_dn) + .encode(properties.get_subject_dn()) .raw_bytes(pub_key) .start_explicit(3) diff --git a/src/lib/cert/x509/x509_ca.h b/src/lib/cert/x509/x509_ca.h index 6ea51cd06..8cedb9db9 100644 --- a/src/lib/cert/x509/x509_ca.h +++ b/src/lib/cert/x509/x509_ca.h @@ -22,6 +22,25 @@ namespace Botan { class BOTAN_DLL X509_CA { public: + class Certificate_Properties + { + public: + Certificate_Properties(X509_Time not_before, X509_Time not_after, + X509_DN issuer_dn, X509_DN subject_dn) + : m_not_before(not_before), m_not_after(not_after), + m_issuer_dn(issuer_dn), m_subject_dn(subject_dn) {} + + const X509_Time& get_not_before() const { return m_not_before; } + const X509_Time& get_not_after() const { return m_not_after; } + const X509_DN& get_issuer_dn() const { return m_issuer_dn; } + const X509_DN& get_subject_dn() const { return m_subject_dn; } + + private: + X509_Time m_not_before; + X509_Time m_not_after; + X509_DN m_issuer_dn; + X509_DN m_subject_dn; + }; /** * Sign a PKCS#10 Request. @@ -82,10 +101,7 @@ class BOTAN_DLL X509_CA RandomNumberGenerator& rng, const AlgorithmIdentifier& sig_algo, const std::vector& pub_key, - const X509_Time& not_before, - const X509_Time& not_after, - const X509_DN& issuer_dn, - const X509_DN& subject_dn, + const Certificate_Properties properties, const Extensions& extensions); /** diff --git a/src/lib/cert/x509/x509self.cpp b/src/lib/cert/x509/x509self.cpp index 8b9aeda09..62f9fc370 100644 --- a/src/lib/cert/x509/x509self.cpp +++ b/src/lib/cert/x509/x509self.cpp @@ -75,9 +75,14 @@ X509_Certificate create_self_signed_cert(const X509_Cert_Options& opts, extensions.add( new Cert_Extension::Extended_Key_Usage(opts.ex_constraints)); - return X509_CA::make_cert(signer.get(), rng, sig_algo, pub_key, - opts.start, opts.end, - subject_dn, subject_dn, + return X509_CA::make_cert(signer.get(), + rng, + sig_algo, + pub_key, + X509_CA::Certificate_Properties(opts.start, + opts.end, + subject_dn, + subject_dn), extensions); } diff --git a/src/lib/tls/info.txt b/src/lib/tls/info.txt index a43d5619a..de15a65e5 100644 --- a/src/lib/tls/info.txt +++ b/src/lib/tls/info.txt @@ -29,6 +29,7 @@ tls_messages.h tls_reader.h tls_record.h tls_seq_numbers.h +tls_server_handshake_state.h tls_session_key.h diff --git a/src/lib/tls/msg_cert_req.cpp b/src/lib/tls/msg_cert_req.cpp index 4fd528148..569a5aa63 100644 --- a/src/lib/tls/msg_cert_req.cpp +++ b/src/lib/tls/msg_cert_req.cpp @@ -1,6 +1,7 @@ /* * Certificate Request Message * (C) 2004-2006,2012 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -51,8 +52,7 @@ byte cert_type_name_to_code(const std::string& name) /** * Create a new Certificate Request message */ -Certificate_Req::Certificate_Req(Handshake_IO& io, - Handshake_Hash& hash, +Certificate_Req::Certificate_Req(Handshake_Info& hs_info, const Policy& policy, const std::vector& ca_certs, Protocol_Version version) : @@ -69,7 +69,7 @@ Certificate_Req::Certificate_Req(Handshake_IO& io, m_supported_algos.push_back(std::make_pair(hashes[i], sigs[j])); } - hash.update(io.send(*this)); + hs_info.get_hash().update(hs_info.get_io().send(*this)); } /** diff --git a/src/lib/tls/msg_certificate.cpp b/src/lib/tls/msg_certificate.cpp index 32e3e17f0..a622d8573 100644 --- a/src/lib/tls/msg_certificate.cpp +++ b/src/lib/tls/msg_certificate.cpp @@ -1,6 +1,7 @@ /* * Certificate Message * (C) 2004-2006,2012 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -20,12 +21,11 @@ namespace TLS { /** * Create a new Certificate message */ -Certificate::Certificate(Handshake_IO& io, - Handshake_Hash& hash, +Certificate::Certificate(Handshake_Info& hs_info, const std::vector& cert_list) : m_certs(cert_list) { - hash.update(io.send(*this)); + hs_info.get_hash().update(hs_info.get_io().send(*this)); } /** diff --git a/src/lib/tls/msg_client_hello.cpp b/src/lib/tls/msg_client_hello.cpp index 23807215f..9c080b9a5 100644 --- a/src/lib/tls/msg_client_hello.cpp +++ b/src/lib/tls/msg_client_hello.cpp @@ -1,6 +1,7 @@ /* * TLS Hello Request and Client Hello Messages * (C) 2004-2011,2015,2016 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -66,24 +67,21 @@ std::vector Hello_Request::serialize() const /* * Create a new Client Hello message */ -Client_Hello::Client_Hello(Handshake_IO& io, - Handshake_Hash& hash, - Protocol_Version version, +Client_Hello::Client_Hello(Handshake_Info& hs_info, const Policy& policy, RandomNumberGenerator& rng, const std::vector& reneg_info, - const std::vector& next_protocols, - const std::string& hostname, - const std::string& srp_identifier) : - m_version(version), + const Client_Hello::Settings& client_settings, + const std::vector& next_protocols) : + m_version(client_settings.protocol_version()), m_random(make_hello_random(rng, policy)), - m_suites(policy.ciphersuite_list(m_version, (srp_identifier != ""))), + m_suites(policy.ciphersuite_list(m_version, + client_settings.srp_identifier() != "")), m_comp_methods(policy.compression()) { m_extensions.add(new Extended_Master_Secret); m_extensions.add(new Renegotiation_Extension(reneg_info)); - - m_extensions.add(new Server_Name_Indicator(hostname)); + m_extensions.add(new Server_Name_Indicator(client_settings.hostname())); m_extensions.add(new Session_Ticket()); m_extensions.add(new Supported_Elliptic_Curves(policy.allowed_ecc_curves())); @@ -98,7 +96,7 @@ Client_Hello::Client_Hello(Handshake_IO& io, m_extensions.add(new Application_Layer_Protocol_Notification(next_protocols)); #if defined(BOTAN_HAS_SRP6) - m_extensions.add(new SRP_Identifier(srp_identifier)); + m_extensions.add(new SRP_Identifier(client_settings.srp_identifier())); #else if(!srp_identifier.empty()) { @@ -106,20 +104,19 @@ Client_Hello::Client_Hello(Handshake_IO& io, } #endif - BOTAN_ASSERT(policy.acceptable_protocol_version(version), + BOTAN_ASSERT(policy.acceptable_protocol_version(client_settings.protocol_version()), "Our policy accepts the version we are offering"); - if(policy.send_fallback_scsv(version)) + if(policy.send_fallback_scsv(client_settings.protocol_version())) m_suites.push_back(TLS_FALLBACK_SCSV); - hash.update(io.send(*this)); + hs_info.get_hash().update(hs_info.get_io().send(*this)); } /* * Create a new Client Hello message (session resumption case) */ -Client_Hello::Client_Hello(Handshake_IO& io, - Handshake_Hash& hash, +Client_Hello::Client_Hello(Handshake_Info& hs_info, const Policy& policy, RandomNumberGenerator& rng, const std::vector& reneg_info, @@ -165,7 +162,7 @@ Client_Hello::Client_Hello(Handshake_IO& io, } #endif - hash.update(io.send(*this)); + hs_info.get_hash().update(hs_info.get_io().send(*this)); } void Client_Hello::update_hello_cookie(const Hello_Verify_Request& hello_verify) diff --git a/src/lib/tls/msg_server_hello.cpp b/src/lib/tls/msg_server_hello.cpp index f8d0c63c7..2c80ed59a 100644 --- a/src/lib/tls/msg_server_hello.cpp +++ b/src/lib/tls/msg_server_hello.cpp @@ -1,6 +1,7 @@ /* * TLS Server Hello and Server Hello Done * (C) 2004-2011,2015,2016 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -17,23 +18,18 @@ namespace Botan { namespace TLS { // New session case -Server_Hello::Server_Hello(Handshake_IO& io, - Handshake_Hash& hash, +Server_Hello::Server_Hello(Handshake_Info& hs_info, const Policy& policy, RandomNumberGenerator& rng, const std::vector& reneg_info, const Client_Hello& client_hello, - const std::vector& new_session_id, - Protocol_Version new_session_version, - u16bit ciphersuite, - byte compression, - bool offer_session_ticket, - const std::string& next_protocol) : - m_version(new_session_version), - m_session_id(new_session_id), + const Server_Hello::Settings& server_settings, + const std::string next_protocol) : + m_version(server_settings.protocol_version()), + m_session_id(server_settings.session_id()), m_random(make_hello_random(rng, policy)), - m_ciphersuite(ciphersuite), - m_comp_method(compression) + m_ciphersuite(server_settings.ciphersuite()), + m_comp_method(server_settings.compression()) { if(client_hello.supports_extended_master_secret()) m_extensions.add(new Extended_Master_Secret); @@ -41,7 +37,7 @@ Server_Hello::Server_Hello(Handshake_IO& io, if(client_hello.secure_renegotiation()) m_extensions.add(new Renegotiation_Extension(reneg_info)); - if(client_hello.supports_session_ticket() && offer_session_ticket) + if(client_hello.supports_session_ticket() && server_settings.offer_session_ticket()) m_extensions.add(new Session_Ticket()); if(!next_protocol.empty() && client_hello.supports_alpn()) @@ -68,12 +64,11 @@ Server_Hello::Server_Hello(Handshake_IO& io, } } - hash.update(io.send(*this)); + hs_info.get_hash().update(hs_info.get_io().send(*this)); } // Resuming -Server_Hello::Server_Hello(Handshake_IO& io, - Handshake_Hash& hash, +Server_Hello::Server_Hello(Handshake_Info& hs_info, const Policy& policy, RandomNumberGenerator& rng, const std::vector& reneg_info, @@ -99,7 +94,7 @@ Server_Hello::Server_Hello(Handshake_IO& io, if(!next_protocol.empty() && client_hello.supports_alpn()) m_extensions.add(new Application_Layer_Protocol_Notification(next_protocol)); - hash.update(io.send(*this)); + hs_info.get_hash().update(hs_info.get_io().send(*this)); } /* diff --git a/src/lib/tls/tls_blocking.cpp b/src/lib/tls/tls_blocking.cpp index a1867b6b5..14e04693d 100644 --- a/src/lib/tls/tls_blocking.cpp +++ b/src/lib/tls/tls_blocking.cpp @@ -1,6 +1,7 @@ /* * TLS Blocking API * (C) 2013 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -19,21 +20,19 @@ Blocking_Client::Blocking_Client(read_fn reader, Credentials_Manager& creds, const Policy& policy, RandomNumberGenerator& rng, - const Server_Information& server_info, - const Protocol_Version& offer_version, - const std::vector& next) : + TLS::Client::Properties& properties) : m_read(reader), - m_channel(writer, - std::bind(&Blocking_Client::data_cb, this, _1, _2), - std::bind(&Blocking_Client::alert_cb, this, _1, _2, _3), - std::bind(&Blocking_Client::handshake_cb, this, _1), + m_channel(TLS::Client::Callbacks( + writer, + std::bind(&Blocking_Client::data_cb, this, _1, _2), + std::bind(&Blocking_Client::alert_cb, this, _1, _2, _3), + std::bind(&Blocking_Client::handshake_cb, this, _1) + ), session_manager, creds, policy, rng, - server_info, - offer_version, - next) + properties) { } diff --git a/src/lib/tls/tls_blocking.h b/src/lib/tls/tls_blocking.h index 00e65cbaf..47c5c7483 100644 --- a/src/lib/tls/tls_blocking.h +++ b/src/lib/tls/tls_blocking.h @@ -1,6 +1,7 @@ /* * TLS Blocking API * (C) 2013 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -38,9 +39,7 @@ class BOTAN_DLL Blocking_Client Credentials_Manager& creds, const Policy& policy, RandomNumberGenerator& rng, - const Server_Information& server_info = Server_Information(), - const Protocol_Version& offer_version = Protocol_Version::latest_tls_version(), - const std::vector& next_protos = {}); + TLS::Client::Properties& properties); /** * Completes full handshake then returns diff --git a/src/lib/tls/tls_channel.cpp b/src/lib/tls/tls_channel.cpp index f445eef99..2c7e80feb 100644 --- a/src/lib/tls/tls_channel.cpp +++ b/src/lib/tls/tls_channel.cpp @@ -1,6 +1,7 @@ /* * TLS Channels * (C) 2011,2012,2014,2015 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -18,22 +19,16 @@ namespace Botan { namespace TLS { -Channel::Channel(output_fn output_fn, - data_cb data_cb, - alert_cb alert_cb, - handshake_cb handshake_cb, - handshake_msg_cb handshake_msg_cb, +size_t TLS::Channel::IO_BUF_DEFAULT_SIZE = 10*1024; + +Channel::Channel(const Callbacks& callbacks, Session_Manager& session_manager, RandomNumberGenerator& rng, const Policy& policy, bool is_datagram, size_t reserved_io_buffer_size) : m_is_datagram(is_datagram), - m_data_cb(data_cb), - m_alert_cb(alert_cb), - m_output_fn(output_fn), - m_handshake_cb(handshake_cb), - m_handshake_msg_cb(handshake_msg_cb), + m_callbacks(callbacks), m_session_manager(session_manager), m_policy(policy), m_rng(rng) @@ -263,23 +258,19 @@ size_t Channel::received_data(const byte input[], size_t input_size) { while(!is_closed() && input_size) { - secure_vector record; + secure_vector record_data; u64bit record_sequence = 0; Record_Type record_type = NO_RECORD; Protocol_Version record_version; size_t consumed = 0; + Record_Raw_Input raw_input(input, input_size, consumed, m_is_datagram); + Record record(record_data, &record_sequence, &record_version, &record_type); const size_t needed = read_record(m_readbuf, - input, - input_size, - m_is_datagram, - consumed, + raw_input, record, - &record_sequence, - &record_version, - &record_type, m_sequence_numbers.get(), std::bind(&TLS::Channel::read_cipher_state_epoch, this, std::placeholders::_1)); @@ -298,105 +289,21 @@ size_t Channel::received_data(const byte input[], size_t input_size) if(input_size == 0 && needed != 0) return needed; // need more data to complete record - if(record.size() > MAX_PLAINTEXT_SIZE) + if(record_data.size() > MAX_PLAINTEXT_SIZE) throw TLS_Exception(Alert::RECORD_OVERFLOW, "TLS plaintext record is larger than allowed maximum"); if(record_type == HANDSHAKE || record_type == CHANGE_CIPHER_SPEC) { - if(!m_pending_state) - { - // No pending handshake, possibly new: - if(record_version.is_datagram_protocol()) - { - if(m_sequence_numbers) - { - /* - * Might be a peer retransmit under epoch - 1 in which - * case we must retransmit last flight - */ - sequence_numbers().read_accept(record_sequence); - - const u16bit epoch = record_sequence >> 48; - - if(epoch == sequence_numbers().current_read_epoch()) - { - create_handshake_state(record_version); - } - else if(epoch == sequence_numbers().current_read_epoch() - 1) - { - BOTAN_ASSERT(m_active_state, "Have active state here"); - m_active_state->handshake_io().add_record(unlock(record), - record_type, - record_sequence); - } - } - else if(record_sequence == 0) - { - create_handshake_state(record_version); - } - } - else - { - create_handshake_state(record_version); - } - } - - // May have been created in above conditional - if(m_pending_state) - { - m_pending_state->handshake_io().add_record(unlock(record), - record_type, - record_sequence); - - while(auto pending = m_pending_state.get()) - { - auto msg = pending->get_next_handshake_msg(); - - if(msg.first == HANDSHAKE_NONE) // no full handshake yet - break; - - process_handshake_msg(active_state(), *pending, - msg.first, msg.second); - } - } + process_handshake_ccs(record_data, record_sequence, record_type, record_version); } else if(record_type == APPLICATION_DATA) { - if(!active_state()) - throw Unexpected_Message("Application data before handshake done"); - - /* - * OpenSSL among others sends empty records in versions - * before TLS v1.1 in order to randomize the IV of the - * following record. Avoid spurious callbacks. - */ - if(record.size() > 0) - m_data_cb(record.data(), record.size()); + process_application_data(record_data); } else if(record_type == ALERT) { - Alert alert_msg(record); - - if(alert_msg.type() == Alert::NO_RENEGOTIATION) - m_pending_state.reset(); - - m_alert_cb(alert_msg, nullptr, 0); - - if(alert_msg.is_fatal()) - { - if(auto active = active_state()) - m_session_manager.remove_entry(active->server_hello()->session_id()); - } - - if(alert_msg.type() == Alert::CLOSE_NOTIFY) - send_warning_alert(Alert::CLOSE_NOTIFY); // reply in kind - - if(alert_msg.type() == Alert::CLOSE_NOTIFY || alert_msg.is_fatal()) - { - reset_state(); - return 0; - } + process_alert(record_data); } else if(record_type != NO_RECORD) throw Unexpected_Message("Unexpected record type " + @@ -428,6 +335,108 @@ size_t Channel::received_data(const byte input[], size_t input_size) } } +void Channel::process_handshake_ccs(secure_vector& record, + u64bit& record_sequence, + Record_Type& record_type, + Protocol_Version& record_version) + { + if(!m_pending_state) + { + // No pending handshake, possibly new: + if(record_version.is_datagram_protocol()) + { + if(m_sequence_numbers) + { + /* + * Might be a peer retransmit under epoch - 1 in which + * case we must retransmit last flight + */ + sequence_numbers().read_accept(record_sequence); + + const u16bit epoch = record_sequence >> 48; + + if(epoch == sequence_numbers().current_read_epoch()) + { + create_handshake_state(record_version); + } + else if(epoch == sequence_numbers().current_read_epoch() - 1) + { + BOTAN_ASSERT(m_active_state, "Have active state here"); + m_active_state->handshake_io().add_record(unlock(record), + record_type, + record_sequence); + } + } + else if(record_sequence == 0) + { + create_handshake_state(record_version); + } + } + else + { + create_handshake_state(record_version); + } + } + + // May have been created in above conditional + if(m_pending_state) + { + m_pending_state->handshake_io().add_record(unlock(record), + record_type, + record_sequence); + + while(auto pending = m_pending_state.get()) + { + auto msg = pending->get_next_handshake_msg(); + + if(msg.first == HANDSHAKE_NONE) // no full handshake yet + break; + + process_handshake_msg(active_state(), *pending, + msg.first, msg.second); + } + } + } + +void Channel::process_application_data(secure_vector& record) + { + if(!active_state()) + throw Unexpected_Message("Application data before handshake done"); + + /* + * OpenSSL among others sends empty records in versions + * before TLS v1.1 in order to randomize the IV of the + * following record. Avoid spurious callbacks. + */ + if(record.size() > 0) + m_callbacks.app_data()(record.data(), record.size()); + } + +void Channel::process_alert(secure_vector& record) + { + Alert alert_msg(record); + + if(alert_msg.type() == Alert::NO_RENEGOTIATION) + m_pending_state.reset(); + + m_callbacks.alert()(alert_msg, nullptr, 0); + + if(alert_msg.is_fatal()) + { + if(auto active = active_state()) + m_session_manager.remove_entry(active->server_hello()->session_id()); + } + + if(alert_msg.type() == Alert::CLOSE_NOTIFY) + send_warning_alert(Alert::CLOSE_NOTIFY); // reply in kind + + if(alert_msg.type() == Alert::CLOSE_NOTIFY || alert_msg.is_fatal()) + { + reset_state(); + } + } + + void Channel::write_record(Connection_Cipher_State* cipher_state, u16bit epoch, byte record_type, const byte input[], size_t length) { @@ -436,16 +445,16 @@ void Channel::write_record(Connection_Cipher_State* cipher_state, u16bit epoch, Protocol_Version record_version = (m_pending_state) ? (m_pending_state->version()) : (m_active_state->version()); + Record_Message record_message(record_type, 0, input, length); + TLS::write_record(m_writebuf, - record_type, - input, - length, + record_message, record_version, sequence_numbers().next_write_sequence(epoch), cipher_state, m_rng); - m_output_fn(m_writebuf.data(), m_writebuf.size()); + m_callbacks.out_fn()(m_writebuf.data(), m_writebuf.size()); } void Channel::send_record_array(u16bit epoch, byte type, const byte input[], size_t length) diff --git a/src/lib/tls/tls_channel.h b/src/lib/tls/tls_channel.h index e0219c242..c9ea8edde 100644 --- a/src/lib/tls/tls_channel.h +++ b/src/lib/tls/tls_channel.h @@ -1,6 +1,7 @@ /* * TLS Channel * (C) 2011,2012,2014,2015 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -32,22 +33,58 @@ class Handshake_Message; class BOTAN_DLL Channel { public: - typedef std::function output_fn; - typedef std::function data_cb; - typedef std::function alert_cb; - typedef std::function handshake_cb; - typedef std::function handshake_msg_cb; - - Channel(output_fn out, - data_cb app_data_cb, - alert_cb alert_cb, - handshake_cb hs_cb, - handshake_msg_cb hs_msg_cb, + static size_t IO_BUF_DEFAULT_SIZE; + + class Callbacks + { + public: + typedef std::function output_fn; + typedef std::function data_cb; + typedef std::function alert_cb; + typedef std::function handshake_cb; + typedef std::function handshake_msg_cb; + /** + * Encapsulates a set of callback functions required by a TLS Channel. + * @param output_fn is called with data for the outbound socket + * + * @param app_data_cb is called when new application data is received + * + * @param alert_cb is called when a TLS alert is received + * + * @param handshake_cb is called when a handshake is completed + */ + Callbacks(output_fn out, data_cb app_data_cb, alert_cb alert_cb, + handshake_cb hs_cb) + : m_output_function(out), m_app_data_cb(app_data_cb), + m_alert_cb(alert_cb), m_hs_cb(hs_cb), m_hs_msg_cb() {} + + Callbacks(output_fn out, data_cb app_data_cb, alert_cb alert_cb, + handshake_cb hs_cb, handshake_msg_cb hs_msg_cb) + : m_output_function(out), m_app_data_cb(app_data_cb), + m_alert_cb(alert_cb), m_hs_cb(hs_cb), m_hs_msg_cb(hs_msg_cb) {} + + const output_fn& out_fn() const { return m_output_function; } + const data_cb& app_data() const { return m_app_data_cb; } + const alert_cb& alert() const { return m_alert_cb; } + const handshake_cb& handshake() const { return m_hs_cb; } + const handshake_msg_cb& handshake_msg() const { return m_hs_msg_cb; } + + private: + const output_fn m_output_function; + const data_cb m_app_data_cb; + const alert_cb m_alert_cb; + const handshake_cb m_hs_cb; + const handshake_msg_cb m_hs_msg_cb; + }; + + + + Channel(const Callbacks& callbacks, Session_Manager& session_manager, RandomNumberGenerator& rng, const Policy& policy, bool is_datagram, - size_t io_buf_sz = 16*1024); + size_t io_buf_sz = IO_BUF_DEFAULT_SIZE); Channel(const Channel&) = delete; @@ -200,9 +237,9 @@ class BOTAN_DLL Channel const Policy& policy() const { return m_policy; } - bool save_session(const Session& session) const { return m_handshake_cb(session); } + bool save_session(const Session& session) const { return m_callbacks.handshake()(session); } - handshake_msg_cb get_handshake_msg_cb() const { return m_handshake_msg_cb; } + Callbacks get_callbacks() const { return m_callbacks; } private: void send_record(byte record_type, const std::vector& record); @@ -227,14 +264,20 @@ class BOTAN_DLL Channel const Handshake_State* pending_state() const { return m_pending_state.get(); } + /* methods to handle incoming traffic through Channel::receive_data. */ + void process_handshake_ccs(secure_vector& record, + u64bit& record_sequence, + Record_Type& record_type, + Protocol_Version& record_version); + + void process_application_data(secure_vector& record); + + void process_alert(secure_vector& record); + bool m_is_datagram; /* callbacks */ - data_cb m_data_cb; - alert_cb m_alert_cb; - output_fn m_output_fn; - handshake_cb m_handshake_cb; - handshake_msg_cb m_handshake_msg_cb; + Callbacks m_callbacks; /* external state */ Session_Manager& m_session_manager; diff --git a/src/lib/tls/tls_ciphersuite.h b/src/lib/tls/tls_ciphersuite.h index 1f646cc7e..71596897c 100644 --- a/src/lib/tls/tls_ciphersuite.h +++ b/src/lib/tls/tls_ciphersuite.h @@ -1,6 +1,7 @@ /* * TLS Cipher Suites * (C) 2004-2011,2012 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -116,31 +117,17 @@ class BOTAN_DLL Ciphersuite private: - - Ciphersuite(u16bit ciphersuite_code, - const char* iana_id, - const char* sig_algo, - const char* kex_algo, - const char* cipher_algo, - size_t cipher_keylen, - size_t nonce_bytes_from_handshake, - size_t nonce_bytes_from_record, - const char* mac_algo, - size_t mac_keylen, - const char* prf_algo) : - m_ciphersuite_code(ciphersuite_code), - m_iana_id(iana_id), - m_sig_algo(sig_algo), - m_kex_algo(kex_algo), - m_prf_algo(prf_algo), - m_cipher_algo(cipher_algo), - m_mac_algo(mac_algo), - m_cipher_keylen(cipher_keylen), - m_nonce_bytes_from_handshake(nonce_bytes_from_handshake), - m_nonce_bytes_from_record(nonce_bytes_from_record), - m_mac_keylen(mac_keylen) - { - } + Ciphersuite(u16bit ciphersuite_code, + const char* iana_id, + const char* sig_algo, + const char* kex_algo, + const char* cipher_algo, + size_t cipher_keylen, + size_t nonce_bytes_from_handshake, + size_t nonce_bytes_from_record, + const char* mac_algo, + size_t mac_keylen, + const char* prf_algo = ""); u16bit m_ciphersuite_code = 0; diff --git a/src/lib/tls/tls_client.cpp b/src/lib/tls/tls_client.cpp index 301c77c6b..ff4b20bbf 100644 --- a/src/lib/tls/tls_client.cpp +++ b/src/lib/tls/tls_client.cpp @@ -1,6 +1,7 @@ /* * TLS Client * (C) 2004-2011,2012,2015,2016 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -42,55 +43,28 @@ class Client_Handshake_State : public Handshake_State /* * TLS Client Constructor */ -Client::Client(output_fn output_fn, - data_cb proc_cb, - alert_cb alert_cb, - handshake_cb handshake_cb, +Client::Client(const Callbacks& callbacks, Session_Manager& session_manager, Credentials_Manager& creds, const Policy& policy, RandomNumberGenerator& rng, - const Server_Information& info, - const Protocol_Version& offer_version, - const std::vector& next_protos, + Properties properties, size_t io_buf_sz) : - Channel(output_fn, proc_cb, alert_cb, handshake_cb, Channel::handshake_msg_cb(), - session_manager, rng, policy, offer_version.is_datagram_protocol(), io_buf_sz), + Channel(callbacks, session_manager, rng, policy, properties.get_protocol_version().is_datagram_protocol(), + io_buf_sz), m_creds(creds), - m_info(info) + m_info(properties.get_server_info()) { const std::string srp_identifier = m_creds.srp_identifier("tls-client", m_info.hostname()); - Handshake_State& state = create_handshake_state(offer_version); - send_client_hello(state, false, offer_version, srp_identifier, next_protos); - } - -Client::Client(output_fn output_fn, - data_cb proc_cb, - alert_cb alert_cb, - handshake_cb handshake_cb, - handshake_msg_cb hs_msg_cb, - Session_Manager& session_manager, - Credentials_Manager& creds, - const Policy& policy, - RandomNumberGenerator& rng, - const Server_Information& info, - const Protocol_Version& offer_version, - const std::vector& next_protos) : - Channel(output_fn, proc_cb, alert_cb, handshake_cb, hs_msg_cb, - session_manager, rng, policy, offer_version.is_datagram_protocol()), - m_creds(creds), - m_info(info) - { - const std::string srp_identifier = m_creds.srp_identifier("tls-client", m_info.hostname()); - - Handshake_State& state = create_handshake_state(offer_version); - send_client_hello(state, false, offer_version, srp_identifier, next_protos); + Handshake_State& state = create_handshake_state(properties.get_protocol_version()); + send_client_hello(state, false, properties.get_protocol_version(), + srp_identifier, properties.get_next_protocol_versions()); } Handshake_State* Client::new_handshake_state(Handshake_IO* io) { - return new Client_Handshake_State(io, get_handshake_msg_cb()); + return new Client_Handshake_State(io, get_callbacks().handshake_msg()); } std::vector @@ -129,9 +103,9 @@ void Client::send_client_hello(Handshake_State& state_base, { if(srp_identifier == "" || session_info.srp_identifier() == srp_identifier) { + Client_Hello::Handshake_Info hs_info(state.handshake_io(), state.hash()); state.client_hello(new Client_Hello( - state.handshake_io(), - state.hash(), + hs_info, policy(), rng(), secure_renegotiation_data_for_client_hello(), @@ -145,16 +119,16 @@ void Client::send_client_hello(Handshake_State& state_base, if(!state.client_hello()) // not resuming { + Client_Hello::Handshake_Info hs_info(state.handshake_io(), state.hash()); + + Client_Hello::Settings client_settings(version, m_info.hostname(), srp_identifier); state.client_hello(new Client_Hello( - state.handshake_io(), - state.hash(), - version, + hs_info, policy(), rng(), secure_renegotiation_data_for_client_hello(), - next_protocols, - m_info.hostname(), - srp_identifier)); + client_settings, + next_protocols)); } secure_renegotiation_check(state.client_hello()); @@ -419,11 +393,10 @@ void Client::process_handshake_msg(const Handshake_State* active_state, "tls-client", m_info.hostname()); - state.client_certs( - new Certificate(state.handshake_io(), - state.hash(), - client_certs) - ); + Certificate::Handshake_Info hs_info(state.handshake_io(), + state.hash()); + + state.client_certs(new Certificate(hs_info, client_certs)); } state.client_kex( @@ -502,20 +475,22 @@ void Client::process_handshake_msg(const Handshake_State* active_state, if(session_id.empty() && !session_ticket.empty()) session_id = make_hello_random(rng(), policy()); + Session::Properties session_properties( + m_info, + "", + state.server_hello()->srtp_profile(), + state.server_hello()->version(), + state.server_hello()->ciphersuite(), + state.server_hello()->compression_method()); + Session session_info( session_id, state.session_keys().master_secret(), - state.server_hello()->version(), - state.server_hello()->ciphersuite(), - state.server_hello()->compression_method(), CLIENT, state.server_hello()->supports_extended_master_secret(), get_peer_cert_chain(state), session_ticket, - m_info, - "", - state.server_hello()->srtp_profile() - ); + session_properties); const bool should_save = save_session(session_info); diff --git a/src/lib/tls/tls_client.h b/src/lib/tls/tls_client.h index 45a741878..e80739010 100644 --- a/src/lib/tls/tls_client.h +++ b/src/lib/tls/tls_client.h @@ -1,6 +1,7 @@ /* * TLS Client * (C) 2004-2011 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -25,13 +26,8 @@ class BOTAN_DLL Client final : public Channel /** * Set up a new TLS client session * - * @param output_fn is called with data for the outbound socket - * - * @param app_data_cb is called when new application data is received - * - * @param alert_cb is called when a TLS alert is received - * - * @param handshake_cb is called when a handshake is completed + * @param callbacks contains a set of callback function references + * required by the TLS client. * * @param session_manager manages session state * @@ -41,44 +37,65 @@ class BOTAN_DLL Client final : public Channel * * @param rng a random number generator * - * @param server_info is identifying information about the TLS server - * - * @param offer_version specifies which version we will offer - * to the TLS server. - * - * @param next_protocols specifies protocols to advertise with ALPN + * @param properties holds server information and protocol related + * properties. * * @param reserved_io_buffer_size This many bytes of memory will * be preallocated for the read and write buffers. Smaller * values just mean reallocations and copies are more likely. */ - Client(output_fn out, - data_cb app_data_cb, - alert_cb alert_cb, - handshake_cb hs_cb, - Session_Manager& session_manager, - Credentials_Manager& creds, - const Policy& policy, - RandomNumberGenerator& rng, - const Server_Information& server_info = Server_Information(), - const Protocol_Version& offer_version = Protocol_Version::latest_tls_version(), - const std::vector& next_protocols = {}, - size_t reserved_io_buffer_size = 16*1024 - ); - - Client(output_fn out, - data_cb app_data_cb, - alert_cb alert_cb, - handshake_cb hs_cb, - handshake_msg_cb hs_msg_cb, - Session_Manager& session_manager, - Credentials_Manager& creds, - const Policy& policy, - RandomNumberGenerator& rng, - const Server_Information& server_info = Server_Information(), - const Protocol_Version& offer_version = Protocol_Version::latest_tls_version(), - const std::vector& next_protocols = {} + class Properties + { + /** + * Stores TLS Client properties. + * + * @param server_info is identifying information about the TLS server + * + * @param protocol_version specifies which version we will offer + * to the TLS server. + * + * @param next_protocols specifies protocols to advertise with ALPN + */ + + public: + Properties(const Server_Information& server_info + = Server_Information(), + const Protocol_Version protocol_version + = Protocol_Version::latest_tls_version(), + const std::vector& next_versions + = {}) + : m_server_info(server_info), + m_protocol_version(protocol_version), + m_next_protocol_versions(next_versions) {} + + const Server_Information& get_server_info() + { + return m_server_info; + } + + const Protocol_Version& get_protocol_version() + { + return m_protocol_version; + } + + const std::vector& get_next_protocol_versions() + { + return m_next_protocol_versions; + } + + private: + const Server_Information& m_server_info; + const Protocol_Version m_protocol_version; + const std::vector& m_next_protocol_versions; + }; + Client(const Callbacks& callbacks, + Session_Manager& session_manager, + Credentials_Manager& creds, + const Policy& policy, + RandomNumberGenerator& rng, + Properties properties, + size_t reserved_io_buffer_size = 16*1024 ); const std::string& application_protocol() const { return m_application_protocol; } diff --git a/src/lib/tls/tls_extensions.h b/src/lib/tls/tls_extensions.h index a5aac0020..cfde0067c 100644 --- a/src/lib/tls/tls_extensions.h +++ b/src/lib/tls/tls_extensions.h @@ -1,6 +1,7 @@ /* * TLS Extensions * (C) 2011,2012,2016 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -34,7 +35,6 @@ enum Handshake_Extension_Type { TLSEXT_SRP_IDENTIFIER = 12, TLSEXT_SIGNATURE_ALGORITHMS = 13, TLSEXT_USE_SRTP = 14, - TLSEXT_HEARTBEAT_SUPPORT = 15, TLSEXT_ALPN = 16, TLSEXT_EXTENDED_MASTER_SECRET = 23, diff --git a/src/lib/tls/tls_handshake_msg.h b/src/lib/tls/tls_handshake_msg.h index 7e527abf4..c53f73a3c 100644 --- a/src/lib/tls/tls_handshake_msg.h +++ b/src/lib/tls/tls_handshake_msg.h @@ -1,6 +1,7 @@ /* * TLS Handshake Message * (C) 2012 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -22,6 +23,19 @@ namespace TLS { class BOTAN_DLL Handshake_Message { public: + class Handshake_Info + { + public: + Handshake_Info(Handshake_IO& io, Handshake_Hash& hash) + : m_io(io), m_hash(hash) {}; + + Handshake_IO& get_io() { return m_io; }; + Handshake_Hash& get_hash() {return m_hash; }; + + private: + Handshake_IO& m_io; + Handshake_Hash& m_hash; + }; std::string type_string() const; virtual Handshake_Type type() const = 0; diff --git a/src/lib/tls/tls_messages.h b/src/lib/tls/tls_messages.h index 3bee89e13..c6f8f9944 100644 --- a/src/lib/tls/tls_messages.h +++ b/src/lib/tls/tls_messages.h @@ -1,6 +1,7 @@ /* * TLS Messages * (C) 2004-2011,2015 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -61,6 +62,26 @@ class Hello_Verify_Request final : public Handshake_Message class Client_Hello final : public Handshake_Message { public: + class Settings + { + public: + Settings(const Protocol_Version version, + const std::string& hostname = "", + const std::string& srp_identifier = "") + : m_new_session_version(version), + m_hostname(hostname), + m_srp_identifier(srp_identifier) {}; + + const Protocol_Version protocol_version() const { return m_new_session_version; }; + const std::string& hostname() const { return m_hostname; }; + const std::string& srp_identifier() const { return m_srp_identifier; } + + private: + const Protocol_Version m_new_session_version; + const std::string m_hostname; + const std::string m_srp_identifier; + }; + Handshake_Type type() const override { return CLIENT_HELLO; } Protocol_Version version() const { return m_version; } @@ -160,18 +181,14 @@ class Client_Hello final : public Handshake_Message std::set extension_types() const { return m_extensions.extension_types(); } - Client_Hello(Handshake_IO& io, - Handshake_Hash& hash, - Protocol_Version version, + Client_Hello(Handshake_Info& hs_info, const Policy& policy, RandomNumberGenerator& rng, const std::vector& reneg_info, - const std::vector& next_protocols, - const std::string& hostname = "", - const std::string& srp_identifier = ""); + const Client_Hello::Settings& client_settings, + const std::vector& next_protocols); - Client_Hello(Handshake_IO& io, - Handshake_Hash& hash, + Client_Hello(Handshake_Info& hs_info, const Policy& policy, RandomNumberGenerator& rng, const std::vector& reneg_info, @@ -199,6 +216,35 @@ class Client_Hello final : public Handshake_Message class Server_Hello final : public Handshake_Message { public: + class Settings + { + public: + Settings(const std::vector new_session_id, + Protocol_Version new_session_version, + u16bit ciphersuite, + byte compression, + bool offer_session_ticket) + : m_new_session_id(new_session_id), + m_new_session_version(new_session_version), + m_ciphersuite(ciphersuite), + m_compression(compression), + m_offer_session_ticket(offer_session_ticket) {}; + + const std::vector& session_id() const { return m_new_session_id; }; + Protocol_Version protocol_version() const { return m_new_session_version; }; + u16bit ciphersuite() const { return m_ciphersuite; }; + byte compression() const { return m_compression; } + bool offer_session_ticket() const { return m_offer_session_ticket; } + + private: + const std::vector m_new_session_id; + Protocol_Version m_new_session_version; + u16bit m_ciphersuite; + byte m_compression; + bool m_offer_session_ticket; + }; + + Handshake_Type type() const override { return SERVER_HELLO; } Protocol_Version version() const { return m_version; } @@ -256,21 +302,15 @@ class Server_Hello final : public Handshake_Message std::set extension_types() const { return m_extensions.extension_types(); } - Server_Hello(Handshake_IO& io, - Handshake_Hash& hash, + Server_Hello(Handshake_Info& hs_info, const Policy& policy, RandomNumberGenerator& rng, const std::vector& secure_reneg_info, const Client_Hello& client_hello, - const std::vector& new_session_id, - Protocol_Version new_session_version, - u16bit ciphersuite, - byte compression, - bool offer_session_ticket, - const std::string& next_protocol); + const Server_Hello::Settings& settings, + const std::string next_protocol); - Server_Hello(Handshake_IO& io, - Handshake_Hash& hash, + Server_Hello(Handshake_Info& hs_info, const Policy& policy, RandomNumberGenerator& rng, const std::vector& secure_reneg_info, @@ -301,7 +341,6 @@ class Client_Key_Exchange final : public Handshake_Message const secure_vector& pre_master_secret() const { return m_pre_master; } - Client_Key_Exchange(Handshake_IO& io, Handshake_State& state, const Policy& policy, @@ -337,8 +376,7 @@ class Certificate final : public Handshake_Message size_t count() const { return m_certs.size(); } bool empty() const { return m_certs.empty(); } - Certificate(Handshake_IO& io, - Handshake_Hash& hash, + Certificate(Handshake_Info& hs_info, const std::vector& certs); explicit Certificate(const std::vector& buf); @@ -364,8 +402,7 @@ class Certificate_Req final : public Handshake_Message std::vector > supported_algos() const { return m_supported_algos; } - Certificate_Req(Handshake_IO& io, - Handshake_Hash& hash, + Certificate_Req(Handshake_Info& hs_info, const Policy& policy, const std::vector& allowed_cas, Protocol_Version version); diff --git a/src/lib/tls/tls_record.cpp b/src/lib/tls/tls_record.cpp index eacf313a8..5fda1fbb4 100644 --- a/src/lib/tls/tls_record.cpp +++ b/src/lib/tls/tls_record.cpp @@ -1,6 +1,7 @@ /* * TLS Record Handling * (C) 2012,2013,2014,2015 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -152,7 +153,7 @@ Connection_Cipher_State::format_ad(u64bit msg_sequence, } void write_record(secure_vector& output, - byte msg_type, const byte msg[], size_t msg_length, + Record_Message msg, Protocol_Version version, u64bit seq, Connection_Cipher_State* cs, @@ -160,7 +161,7 @@ void write_record(secure_vector& output, { output.clear(); - output.push_back(msg_type); + output.push_back(msg.get_type()); output.push_back(version.major_version()); output.push_back(version.minor_version()); @@ -172,17 +173,17 @@ void write_record(secure_vector& output, if(!cs) // initial unencrypted handshake records { - output.push_back(get_byte(0, static_cast(msg_length))); - output.push_back(get_byte(1, static_cast(msg_length))); + output.push_back(get_byte(0, static_cast(msg.get_size()))); + output.push_back(get_byte(1, static_cast(msg.get_size()))); - output.insert(output.end(), msg, msg + msg_length); + output.insert(output.end(), msg.get_data(), msg.get_data() + msg.get_size()); return; } if(AEAD_Mode* aead = cs->aead()) { - const size_t ctext_size = aead->output_length(msg_length); + const size_t ctext_size = aead->output_length(msg.get_size()); const std::vector nonce = cs->aead_nonce(seq); @@ -193,17 +194,16 @@ void write_record(secure_vector& output, output.push_back(get_byte(0, static_cast(rec_size))); output.push_back(get_byte(1, static_cast(rec_size))); - aead->set_ad(cs->format_ad(seq, msg_type, version, static_cast(msg_length))); + aead->set_ad(cs->format_ad(seq, msg.get_type(), version, static_cast(msg.get_size()))); if(cs->nonce_bytes_from_record() > 0) { output += std::make_pair(&nonce[cs->nonce_bytes_from_handshake()], cs->nonce_bytes_from_record()); } - BOTAN_ASSERT(aead->start(nonce).empty(), "AEAD doesn't return anything from start"); const size_t offset = output.size(); - output += std::make_pair(msg, msg_length); + output += std::make_pair(msg.get_data(), msg.get_size()); aead->finish(output, offset); BOTAN_ASSERT(output.size() == offset + ctext_size, "Expected size"); @@ -213,16 +213,16 @@ void write_record(secure_vector& output, return; } - cs->mac()->update(cs->format_ad(seq, msg_type, version, static_cast(msg_length))); + cs->mac()->update(cs->format_ad(seq, msg.get_type(), version, static_cast(msg.get_size()))); - cs->mac()->update(msg, msg_length); + cs->mac()->update(msg.get_data(), msg.get_size()); const size_t block_size = cs->block_size(); const size_t iv_size = cs->iv_size(); const size_t mac_size = cs->mac_size(); const size_t buf_size = round_up( - iv_size + msg_length + mac_size + (block_size ? 1 : 0), + iv_size + msg.get_size() + mac_size + (block_size ? 1 : 0), block_size); if(buf_size > MAX_CIPHERTEXT_SIZE) @@ -239,7 +239,7 @@ void write_record(secure_vector& output, rng.randomize(&output[output.size() - iv_size], iv_size); } - output.insert(output.end(), msg, msg + msg_length); + output.insert(output.end(), msg.get_data(), msg.get_data() + msg.get_size()); output.resize(output.size() + mac_size); cs->mac()->final(&output[output.size() - mac_size]); @@ -247,7 +247,7 @@ void write_record(secure_vector& output, if(block_size) { const size_t pad_val = - buf_size - (iv_size + msg_length + mac_size + 1); + buf_size - (iv_size + msg.get_size() + mac_size + 1); for(size_t i = 0; i != pad_val + 1; ++i) output.push_back(static_cast(pad_val)); @@ -461,65 +461,58 @@ void decrypt_record(secure_vector& output, } size_t read_tls_record(secure_vector& readbuf, - const byte input[], - size_t input_sz, - size_t& consumed, - secure_vector& record, - u64bit* record_sequence, - Protocol_Version* record_version, - Record_Type* record_type, + Record_Raw_Input& raw_input, + Record& rec, Connection_Sequence_Numbers* sequence_numbers, get_cipherstate_fn get_cipherstate) { - consumed = 0; - if(readbuf.size() < TLS_HEADER_SIZE) // header incomplete? { if(size_t needed = fill_buffer_to(readbuf, - input, input_sz, consumed, + raw_input.get_data(), raw_input.get_size(), raw_input.get_consumed(), TLS_HEADER_SIZE)) return needed; BOTAN_ASSERT_EQUAL(readbuf.size(), TLS_HEADER_SIZE, "Have an entire header"); } - *record_version = Protocol_Version(readbuf[1], readbuf[2]); + *rec.get_protocol_version() = Protocol_Version(readbuf[1], readbuf[2]); - BOTAN_ASSERT(!record_version->is_datagram_protocol(), "Expected TLS"); + BOTAN_ASSERT(!rec.get_protocol_version()->is_datagram_protocol(), "Expected TLS"); - const size_t record_len = make_u16bit(readbuf[TLS_HEADER_SIZE-2], + const size_t record_size = make_u16bit(readbuf[TLS_HEADER_SIZE-2], readbuf[TLS_HEADER_SIZE-1]); - if(record_len > MAX_CIPHERTEXT_SIZE) + if(record_size > MAX_CIPHERTEXT_SIZE) throw TLS_Exception(Alert::RECORD_OVERFLOW, "Received a record that exceeds maximum size"); - if(record_len == 0) + if(record_size == 0) throw TLS_Exception(Alert::DECODE_ERROR, "Received a completely empty record"); if(size_t needed = fill_buffer_to(readbuf, - input, input_sz, consumed, - TLS_HEADER_SIZE + record_len)) + raw_input.get_data(), raw_input.get_size(), raw_input.get_consumed(), + TLS_HEADER_SIZE + record_size)) return needed; - BOTAN_ASSERT_EQUAL(static_cast(TLS_HEADER_SIZE) + record_len, + BOTAN_ASSERT_EQUAL(static_cast(TLS_HEADER_SIZE) + record_size, readbuf.size(), "Have the full record"); - *record_type = static_cast(readbuf[0]); + *rec.get_type() = static_cast(readbuf[0]); u16bit epoch = 0; if(sequence_numbers) { - *record_sequence = sequence_numbers->next_read_sequence(); + *rec.get_sequence() = sequence_numbers->next_read_sequence(); epoch = sequence_numbers->current_read_epoch(); } else { // server initial handshake case - *record_sequence = 0; + *rec.get_sequence() = 0; epoch = 0; } @@ -527,7 +520,7 @@ size_t read_tls_record(secure_vector& readbuf, if(epoch == 0) // Unencrypted initial handshake { - record.assign(readbuf.begin() + TLS_HEADER_SIZE, readbuf.begin() + TLS_HEADER_SIZE + record_len); + rec.get_data().assign(readbuf.begin() + TLS_HEADER_SIZE, readbuf.begin() + TLS_HEADER_SIZE + record_size); readbuf.clear(); return 0; // got a full record } @@ -537,37 +530,30 @@ size_t read_tls_record(secure_vector& readbuf, BOTAN_ASSERT(cs, "Have cipherstate for this epoch"); - decrypt_record(record, + decrypt_record(rec.get_data(), record_contents, - record_len, - *record_sequence, - *record_version, - *record_type, + record_size, + *rec.get_sequence(), + *rec.get_protocol_version(), + *rec.get_type(), *cs); if(sequence_numbers) - sequence_numbers->read_accept(*record_sequence); + sequence_numbers->read_accept(*rec.get_sequence()); readbuf.clear(); return 0; } size_t read_dtls_record(secure_vector& readbuf, - const byte input[], - size_t input_sz, - size_t& consumed, - secure_vector& record, - u64bit* record_sequence, - Protocol_Version* record_version, - Record_Type* record_type, + Record_Raw_Input& raw_input, + Record& rec, Connection_Sequence_Numbers* sequence_numbers, get_cipherstate_fn get_cipherstate) { - consumed = 0; - if(readbuf.size() < DTLS_HEADER_SIZE) // header incomplete? { - if(fill_buffer_to(readbuf, input, input_sz, consumed, DTLS_HEADER_SIZE)) + if(fill_buffer_to(readbuf, raw_input.get_data(), raw_input.get_size(), raw_input.get_consumed(), DTLS_HEADER_SIZE)) { readbuf.clear(); return 0; @@ -576,38 +562,35 @@ size_t read_dtls_record(secure_vector& readbuf, BOTAN_ASSERT_EQUAL(readbuf.size(), DTLS_HEADER_SIZE, "Have an entire header"); } - *record_version = Protocol_Version(readbuf[1], readbuf[2]); + *rec.get_protocol_version() = Protocol_Version(readbuf[1], readbuf[2]); - BOTAN_ASSERT(record_version->is_datagram_protocol(), "Expected DTLS"); + BOTAN_ASSERT(rec.get_protocol_version()->is_datagram_protocol(), "Expected DTLS"); - const size_t record_len = make_u16bit(readbuf[DTLS_HEADER_SIZE-2], + const size_t record_size = make_u16bit(readbuf[DTLS_HEADER_SIZE-2], readbuf[DTLS_HEADER_SIZE-1]); - // Invalid packet: - if(record_len == 0 || record_len > MAX_CIPHERTEXT_SIZE) - { - readbuf.clear(); - return 0; - } + if(record_size > MAX_CIPHERTEXT_SIZE) + throw TLS_Exception(Alert::RECORD_OVERFLOW, + "Got message that exceeds maximum size"); - if(fill_buffer_to(readbuf, input, input_sz, consumed, DTLS_HEADER_SIZE + record_len)) + if(fill_buffer_to(readbuf, raw_input.get_data(), raw_input.get_size(), raw_input.get_consumed(), DTLS_HEADER_SIZE + record_size)) { // Truncated packet? readbuf.clear(); return 0; } - BOTAN_ASSERT_EQUAL(static_cast(DTLS_HEADER_SIZE) + record_len, readbuf.size(), + BOTAN_ASSERT_EQUAL(static_cast(DTLS_HEADER_SIZE) + record_size, readbuf.size(), "Have the full record"); - *record_type = static_cast(readbuf[0]); + *rec.get_type() = static_cast(readbuf[0]); u16bit epoch = 0; - *record_sequence = load_be(&readbuf[3], 0); - epoch = (*record_sequence >> 48); + *rec.get_sequence() = load_be(&readbuf[3], 0); + epoch = (*rec.get_sequence() >> 48); - if(sequence_numbers && sequence_numbers->already_seen(*record_sequence)) + if(sequence_numbers && sequence_numbers->already_seen(*rec.get_sequence())) { readbuf.clear(); return 0; @@ -617,7 +600,7 @@ size_t read_dtls_record(secure_vector& readbuf, if(epoch == 0) // Unencrypted initial handshake { - record.assign(readbuf.begin() + DTLS_HEADER_SIZE, readbuf.begin() + DTLS_HEADER_SIZE + record_len); + rec.get_data().assign(readbuf.begin() + DTLS_HEADER_SIZE, readbuf.begin() + DTLS_HEADER_SIZE + record_size); readbuf.clear(); return 0; // got a full record } @@ -629,23 +612,23 @@ size_t read_dtls_record(secure_vector& readbuf, BOTAN_ASSERT(cs, "Have cipherstate for this epoch"); - decrypt_record(record, + decrypt_record(rec.get_data(), record_contents, - record_len, - *record_sequence, - *record_version, - *record_type, + record_size, + *rec.get_sequence(), + *rec.get_protocol_version(), + *rec.get_type(), *cs); } catch(std::exception) { readbuf.clear(); - *record_type = NO_RECORD; + *rec.get_type() = NO_RECORD; return 0; } if(sequence_numbers) - sequence_numbers->read_accept(*record_sequence); + sequence_numbers->read_accept(*rec.get_sequence()); readbuf.clear(); return 0; @@ -654,24 +637,16 @@ size_t read_dtls_record(secure_vector& readbuf, } size_t read_record(secure_vector& readbuf, - const byte input[], - size_t input_sz, - bool is_datagram, - size_t& consumed, - secure_vector& record, - u64bit* record_sequence, - Protocol_Version* record_version, - Record_Type* record_type, + Record_Raw_Input& raw_input, + Record& rec, Connection_Sequence_Numbers* sequence_numbers, get_cipherstate_fn get_cipherstate) { - if(is_datagram) - return read_dtls_record(readbuf, input, input_sz, consumed, - record, record_sequence, record_version, record_type, + if(raw_input.is_datagram()) + return read_dtls_record(readbuf, raw_input, rec, sequence_numbers, get_cipherstate); else - return read_tls_record(readbuf, input, input_sz, consumed, - record, record_sequence, record_version, record_type, + return read_tls_record(readbuf, raw_input, rec, sequence_numbers, get_cipherstate); } diff --git a/src/lib/tls/tls_record.h b/src/lib/tls/tls_record.h index e3b0b9b58..53e447af6 100644 --- a/src/lib/tls/tls_record.h +++ b/src/lib/tls/tls_record.h @@ -1,6 +1,7 @@ /* * TLS Record Handling * (C) 2004-2012 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -90,6 +91,84 @@ class Connection_Cipher_State size_t m_iv_size = 0; }; +class Record + { + public: + Record(secure_vector& data, + u64bit* sequence, + Protocol_Version* protocol_version, + Record_Type* type) + : m_data(data), m_sequence(sequence), m_protocol_version(protocol_version), + m_type(type), m_size(data.size()) {}; + + secure_vector& get_data() { return m_data; } + void set_data(secure_vector data) + { + m_data.swap(data); + } + + Protocol_Version* get_protocol_version() { return m_protocol_version; } + + u64bit* get_sequence() { return m_sequence; } + + Record_Type* get_type() { return m_type; } + + size_t& get_size() { return m_size; } + + private: + secure_vector& m_data; + u64bit* m_sequence; + Protocol_Version* m_protocol_version; + Record_Type* m_type; + size_t m_size; + }; + +class Record_Message + { + public: + Record_Message(const byte* data, size_t size) + : m_type(0), m_sequence(0), m_data(data), m_size(size) {}; + Record_Message(byte type, u64bit sequence, const byte* data, size_t size) + : m_type(type), m_sequence(sequence), m_data(data), + m_size(size) {}; + + byte& get_type() { return m_type; }; + u64bit& get_sequence() { return m_sequence; }; + const byte* get_data() { return m_data; }; + size_t& get_size() { return m_size; }; + + private: + byte m_type; + u64bit m_sequence; + const byte* m_data; + size_t m_size; +}; + +class Record_Raw_Input + { + public: + Record_Raw_Input(const byte* data, size_t size, size_t& consumed, + bool is_datagram) + : m_data(data), m_size(size), m_consumed(consumed), + m_is_datagram(is_datagram) {}; + + const byte*& get_data() { return m_data; }; + + size_t& get_size() { return m_size; }; + + size_t& get_consumed() { return m_consumed; }; + void set_consumed(size_t consumed) { m_consumed = consumed; } + + bool is_datagram() { return m_is_datagram; }; + + private: + const byte* m_data; + size_t m_size; + size_t& m_consumed; + bool m_is_datagram; + }; + + /** * Create a TLS record * @param write_buffer the output record is placed here @@ -103,7 +182,7 @@ class Connection_Cipher_State * @return number of bytes written to write_buffer */ void write_record(secure_vector& write_buffer, - byte msg_type, const byte msg[], size_t msg_length, + Record_Message rec_msg, Protocol_Version version, u64bit msg_sequence, Connection_Cipher_State* cipherstate, @@ -117,14 +196,8 @@ typedef std::function (u16bit)> get_cip * @return zero if full message, else number of bytes still needed */ size_t read_record(secure_vector& read_buffer, - const byte input[], - size_t input_length, - bool is_datagram, - size_t& input_consumed, - secure_vector& record, - u64bit* record_sequence, - Protocol_Version* record_version, - Record_Type* record_type, + Record_Raw_Input& raw_input, + Record& rec, Connection_Sequence_Numbers* sequence_numbers, get_cipherstate_fn get_cipherstate); diff --git a/src/lib/tls/tls_server.cpp b/src/lib/tls/tls_server.cpp index 41b14ae08..de1e8668d 100644 --- a/src/lib/tls/tls_server.cpp +++ b/src/lib/tls/tls_server.cpp @@ -1,6 +1,7 @@ /* * TLS Server * (C) 2004-2011,2012,2016 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -9,6 +10,7 @@ #include #include #include +#include "tls_magic.h" namespace Botan { @@ -16,22 +18,7 @@ namespace TLS { namespace { -class Server_Handshake_State : public Handshake_State - { - public: - // using Handshake_State::Handshake_State; - - Server_Handshake_State(Handshake_IO* io, handshake_msg_cb cb) : Handshake_State(io, cb) {} - // Used by the server only, in case of RSA key exchange. Not owned - Private_Key* server_rsa_kex_key = nullptr; - - /* - * Used by the server to know if resumption should be allowed on - * a server-initiated renegotiation - */ - bool allow_session_resumption = true; - }; bool check_for_resume(Session& session_info, Session_Manager& session_manager, @@ -225,10 +212,7 @@ get_server_certs(const std::string& hostname, /* * TLS Server Constructor */ -Server::Server(output_fn output, - data_cb data_cb, - alert_cb alert_cb, - handshake_cb handshake_cb, +Server::Server(const Callbacks& callbacks, Session_Manager& session_manager, Credentials_Manager& creds, const Policy& policy, @@ -236,26 +220,8 @@ Server::Server(output_fn output, next_protocol_fn next_proto, bool is_datagram, size_t io_buf_sz) : - Channel(output, data_cb, alert_cb, handshake_cb, Channel::handshake_msg_cb(), - session_manager, rng, policy, is_datagram, io_buf_sz), - m_creds(creds), - m_choose_next_protocol(next_proto) - { - } - -Server::Server(output_fn output, - data_cb data_cb, - alert_cb alert_cb, - handshake_cb handshake_cb, - handshake_msg_cb hs_msg_cb, - Session_Manager& session_manager, - Credentials_Manager& creds, - const Policy& policy, - RandomNumberGenerator& rng, - next_protocol_fn next_proto, - bool is_datagram) : - Channel(output, data_cb, alert_cb, handshake_cb, hs_msg_cb, - session_manager, rng, policy, is_datagram), + Channel(callbacks, session_manager, rng, policy, + is_datagram, io_buf_sz), m_creds(creds), m_choose_next_protocol(next_proto) { @@ -264,7 +230,7 @@ Server::Server(output_fn output, Handshake_State* Server::new_handshake_state(Handshake_IO* io) { std::unique_ptr state( - new Server_Handshake_State(io, get_handshake_msg_cb())); + new Server_Handshake_State(io, get_callbacks().handshake_msg())); state->set_expected_next(CLIENT_HELLO); return state.release(); @@ -284,441 +250,513 @@ Server::get_peer_cert_chain(const Handshake_State& state) const void Server::initiate_handshake(Handshake_State& state, bool force_full_renegotiation) { - dynamic_cast(state).allow_session_resumption = - !force_full_renegotiation; + dynamic_cast(state). + set_allow_session_resumption(!force_full_renegotiation); Hello_Request hello_req(state.handshake_io()); } /* -* Process a handshake message +* Process a CLIENT HELLO Message */ -void Server::process_handshake_msg(const Handshake_State* active_state, - Handshake_State& state_base, - Handshake_Type type, - const std::vector& contents) - { - Server_Handshake_State& state = dynamic_cast(state_base); - - state.confirm_transition_to(type); - - /* - * The change cipher spec message isn't technically a handshake - * message so it's not included in the hash. The finished and - * certificate verify messages are verified based on the current - * state of the hash *before* this message so we delay adding them - * to the hash computation until we've processed them below. - */ - if(type != HANDSHAKE_CCS && type != FINISHED && type != CERTIFICATE_VERIFY) +void Server::process_client_hello_msg(const Handshake_State* active_state, + Server_Handshake_State& pending_state, + const std::vector& contents) +{ + const bool initial_handshake = !active_state; + + if(!policy().allow_insecure_renegotiation() && + !(initial_handshake || secure_renegotiation_supported())) { - state.hash().update(state.handshake_io().format(contents, type)); + send_warning_alert(Alert::NO_RENEGOTIATION); + return; } - if(type == CLIENT_HELLO) + pending_state.client_hello(new Client_Hello(contents)); + const Protocol_Version client_version = pending_state.client_hello()->version(); + + Protocol_Version negotiated_version; + + const Protocol_Version latest_supported = + policy().latest_supported_version(client_version.is_datagram_protocol()); + + if((initial_handshake && client_version.known_version()) || + (!initial_handshake && client_version == active_state->version())) { - const bool initial_handshake = !active_state; + /* + Common cases: new client hello with some known version, or a + renegotiation using the same version as previously + negotiated. + */ - if(!policy().allow_insecure_renegotiation() && - !(initial_handshake || secure_renegotiation_supported())) + negotiated_version = client_version; + } + else if(!initial_handshake && (client_version != active_state->version())) + { + /* + * If this is a renegotiation, and the client has offered a + * later version than what it initially negotiated, negotiate + * the old version. This matches OpenSSL's behavior. If the + * client is offering a version earlier than what it initially + * negotiated, reject as a probable attack. + */ + if(active_state->version() > client_version) { - send_warning_alert(Alert::NO_RENEGOTIATION); - return; + throw TLS_Exception(Alert::PROTOCOL_VERSION, + "Client negotiated " + + active_state->version().to_string() + + " then renegotiated with " + + client_version.to_string()); } + else + negotiated_version = active_state->version(); + } + else + { + /* + New negotiation using a version we don't know. Offer them the + best we currently know and support + */ + negotiated_version = latest_supported; + } - state.client_hello(new Client_Hello(contents)); + if(!policy().acceptable_protocol_version(negotiated_version)) + { + throw TLS_Exception(Alert::PROTOCOL_VERSION, + "Client version " + negotiated_version.to_string() + + " is unacceptable by policy"); + } - const Protocol_Version client_version = state.client_hello()->version(); + if(pending_state.client_hello()->sent_fallback_scsv()) + { + if(latest_supported > client_version) + throw TLS_Exception(Alert::INAPPROPRIATE_FALLBACK, + "Client signalled fallback SCSV, possible attack"); + } - Protocol_Version negotiated_version; + secure_renegotiation_check(pending_state.client_hello()); - const Protocol_Version latest_supported = - policy().latest_supported_version(client_version.is_datagram_protocol()); + pending_state.set_version(negotiated_version); - if((initial_handshake && client_version.known_version()) || - (!initial_handshake && client_version == active_state->version())) - { - /* - Common cases: new client hello with some known version, or a - renegotiation using the same version as previously - negotiated. - */ + Session session_info; + const bool resuming = + pending_state.allow_session_resumption() && + check_for_resume(session_info, + session_manager(), + m_creds, + pending_state.client_hello(), + std::chrono::seconds(policy().session_ticket_lifetime())); - negotiated_version = client_version; - } - else if(!initial_handshake && (client_version != active_state->version())) - { - /* - * If this is a renegotiation, and the client has offered a - * later version than what it initially negotiated, negotiate - * the old version. This matches OpenSSL's behavior. If the - * client is offering a version earlier than what it initially - * negotiated, reject as a probable attack. - */ - if(active_state->version() > client_version) - { - throw TLS_Exception(Alert::PROTOCOL_VERSION, - "Client negotiated " + - active_state->version().to_string() + - " then renegotiated with " + - client_version.to_string()); - } - else - negotiated_version = active_state->version(); - } - else - { - /* - New negotiation using a version we don't know. Offer them the - best we currently know and support - */ - negotiated_version = latest_supported; - } + bool have_session_ticket_key = false; - if(!policy().acceptable_protocol_version(negotiated_version)) - { - throw TLS_Exception(Alert::PROTOCOL_VERSION, - "Client version " + negotiated_version.to_string() + - " is unacceptable by policy"); - } + try + { + have_session_ticket_key = + m_creds.psk("tls-server", "session-ticket", "").length() > 0; + } + catch(...) {} - if(state.client_hello()->sent_fallback_scsv()) - { - if(latest_supported > client_version) - throw TLS_Exception(Alert::INAPPROPRIATE_FALLBACK, - "Client signalled fallback SCSV, possible attack"); - } + m_next_protocol = ""; + if(m_choose_next_protocol && pending_state.client_hello()->supports_alpn()) + m_next_protocol = m_choose_next_protocol(pending_state.client_hello()->next_protocols()); - secure_renegotiation_check(state.client_hello()); + if(resuming) + { + this->session_resume(pending_state, have_session_ticket_key, session_info); + } + else // new session + { + this->session_create(pending_state, have_session_ticket_key); + } +} - state.set_version(negotiated_version); +void Server::process_certificate_msg(Server_Handshake_State& pending_state, + const std::vector& contents) +{ + pending_state.client_certs(new Certificate(contents)); + pending_state.set_expected_next(CLIENT_KEX); +} - Session session_info; - const bool resuming = - state.allow_session_resumption && - check_for_resume(session_info, - session_manager(), - m_creds, - state.client_hello(), - std::chrono::seconds(policy().session_ticket_lifetime())); +void Server::process_client_key_exchange_msg(Server_Handshake_State& pending_state, + const std::vector& contents) +{ + if(pending_state.received_handshake_msg(CERTIFICATE) && !pending_state.client_certs()->empty()) + pending_state.set_expected_next(CERTIFICATE_VERIFY); + else + pending_state.set_expected_next(HANDSHAKE_CCS); - bool have_session_ticket_key = false; + pending_state.client_kex( + new Client_Key_Exchange(contents, pending_state, + pending_state.server_rsa_kex_key(), + m_creds, policy(), rng()) + ); - try - { - have_session_ticket_key = - m_creds.psk("tls-server", "session-ticket", "").length() > 0; - } - catch(...) {} + pending_state.compute_session_keys(); +} - m_next_protocol = ""; - if(m_choose_next_protocol && state.client_hello()->supports_alpn()) - m_next_protocol = m_choose_next_protocol(state.client_hello()->next_protocols()); +void Server::process_change_cipher_spec_msg(Server_Handshake_State& pending_state) +{ + pending_state.set_expected_next(FINISHED); + change_cipher_spec_reader(SERVER); +} - if(resuming) - { - // Only offer a resuming client a new ticket if they didn't send one this time, - // ie, resumed via server-side resumption. TODO: also send one if expiring soon? - - const bool offer_new_session_ticket = - (state.client_hello()->supports_session_ticket() && - state.client_hello()->session_ticket().empty() && - have_session_ticket_key); - - state.server_hello(new Server_Hello( - state.handshake_io(), - state.hash(), - policy(), - rng(), - secure_renegotiation_data_for_server_hello(), - *state.client_hello(), - session_info, - offer_new_session_ticket, - m_next_protocol - )); - - secure_renegotiation_check(state.server_hello()); - - state.compute_session_keys(session_info.master_secret()); - - if(!save_session(session_info)) +void Server::process_certificate_verify_msg(Server_Handshake_State& pending_state, + Handshake_Type type, + const std::vector& contents) +{ + pending_state.client_verify ( new Certificate_Verify ( contents, pending_state.version() ) ); + + const std::vector& client_certs = + pending_state.client_certs()->cert_chain(); + + const bool sig_valid = + pending_state.client_verify()->verify ( client_certs[0], pending_state, policy() ); + + pending_state.hash().update ( pending_state.handshake_io().format ( contents, type ) ); + + /* + * Using DECRYPT_ERROR looks weird here, but per RFC 4346 is for + * "A handshake cryptographic operation failed, including being + * unable to correctly verify a signature, ..." + */ + if ( !sig_valid ) + throw TLS_Exception ( Alert::DECRYPT_ERROR, "Client cert verify failed" ); + + try + { + m_creds.verify_certificate_chain ( "tls-server", "", client_certs ); + } + catch ( std::exception& e ) + { + throw TLS_Exception ( Alert::BAD_CERTIFICATE, e.what() ); + } + + pending_state.set_expected_next ( HANDSHAKE_CCS ); +} + +void Server::process_finished_msg(Server_Handshake_State& pending_state, + Handshake_Type type, + const std::vector& contents) +{ + pending_state.set_expected_next ( HANDSHAKE_NONE ); + + pending_state.client_finished ( new Finished ( contents ) ); + + if ( !pending_state.client_finished()->verify ( pending_state, CLIENT ) ) + throw TLS_Exception ( Alert::DECRYPT_ERROR, + "Finished message didn't verify" ); + + if ( !pending_state.server_finished() ) + { + // already sent finished if resuming, so this is a new session + + pending_state.hash().update ( pending_state.handshake_io().format ( contents, type ) ); + + Session::Properties session_properties( + Server_Information(pending_state.client_hello()->sni_hostname()), + "", + pending_state.server_hello()->srtp_profile(), + pending_state.server_hello()->version(), + pending_state.server_hello()->ciphersuite(), + pending_state.server_hello()->compression_method()); + + + Session session_info( + pending_state.server_hello()->session_id(), + pending_state.session_keys().master_secret(), + SERVER, + pending_state.server_hello()->supports_extended_master_secret(), + get_peer_cert_chain ( pending_state ), + std::vector(), + session_properties); + + if ( save_session ( session_info ) ) { - session_manager().remove_entry(session_info.session_id()); - - if(state.server_hello()->supports_session_ticket()) // send an empty ticket - { - state.new_session_ticket( - new New_Session_Ticket(state.handshake_io(), - state.hash()) - ); - } + if ( pending_state.server_hello()->supports_session_ticket() ) + { + try + { + const SymmetricKey ticket_key = m_creds.psk ( "tls-server", "session-ticket", "" ); + + pending_state.new_session_ticket ( + new New_Session_Ticket ( pending_state.handshake_io(), + pending_state.hash(), + session_info.encrypt ( ticket_key, rng() ), + policy().session_ticket_lifetime() ) + ); + } + catch ( ... ) {} + } + else + session_manager().save ( session_info ); } - if(state.server_hello()->supports_session_ticket() && !state.new_session_ticket()) + if ( !pending_state.new_session_ticket() && + pending_state.server_hello()->supports_session_ticket() ) { - try - { - const SymmetricKey ticket_key = m_creds.psk("tls-server", "session-ticket", ""); - - state.new_session_ticket( - new New_Session_Ticket(state.handshake_io(), - state.hash(), - session_info.encrypt(ticket_key, rng()), - policy().session_ticket_lifetime()) - ); - } - catch(...) {} - - if(!state.new_session_ticket()) - { - state.new_session_ticket( - new New_Session_Ticket(state.handshake_io(), state.hash()) - ); - } + pending_state.new_session_ticket ( + new New_Session_Ticket ( pending_state.handshake_io(), pending_state.hash() ) + ); } - state.handshake_io().send(Change_Cipher_Spec()); + pending_state.handshake_io().send ( Change_Cipher_Spec() ); - change_cipher_spec_writer(SERVER); + change_cipher_spec_writer ( SERVER ); - state.server_finished(new Finished(state.handshake_io(), state, SERVER)); - state.set_expected_next(HANDSHAKE_CCS); - } - else // new session - { - std::map > cert_chains; + pending_state.server_finished ( new Finished ( pending_state.handshake_io(), pending_state, SERVER ) ); + } - const std::string sni_hostname = state.client_hello()->sni_hostname(); + activate_session(); - cert_chains = get_server_certs(sni_hostname, m_creds); +} - if(sni_hostname != "" && cert_chains.empty()) - { - cert_chains = get_server_certs("", m_creds); - - /* - * Only send the unrecognized_name alert if we couldn't - * find any certs for the requested name but did find at - * least one cert to use in general. That avoids sending an - * unrecognized_name when a server is configured for purely - * anonymous operation. - */ - if(!cert_chains.empty()) - send_alert(Alert(Alert::UNRECOGNIZED_NAME)); - } +/* +* Process a handshake message +*/ +void Server::process_handshake_msg(const Handshake_State* active_state, + Handshake_State& state_base, + Handshake_Type type, + const std::vector& contents) + { + Server_Handshake_State& state = dynamic_cast(state_base); + state.confirm_transition_to(type); - state.server_hello(new Server_Hello( - state.handshake_io(), - state.hash(), - policy(), - rng(), - secure_renegotiation_data_for_server_hello(), - *state.client_hello(), - make_hello_random(rng(), policy()), // new session ID - state.version(), - choose_ciphersuite(policy(), state.version(), m_creds, cert_chains, state.client_hello()), - choose_compression(policy(), state.client_hello()->compression_methods()), - have_session_ticket_key, - m_next_protocol) - ); + /* + * The change cipher spec message isn't technically a handshake + * message so it's not included in the hash. The finished and + * certificate verify messages are verified based on the current + * state of the hash *before* this message so we delay adding them + * to the hash computation until we've processed them below. + */ + if(type != HANDSHAKE_CCS && type != FINISHED && type != CERTIFICATE_VERIFY) + { + state.hash().update(state.handshake_io().format(contents, type)); + } - secure_renegotiation_check(state.server_hello()); + switch(type) + { + case CLIENT_HELLO: + this->process_client_hello_msg(active_state, state, contents); + break; - const std::string sig_algo = state.ciphersuite().sig_algo(); - const std::string kex_algo = state.ciphersuite().kex_algo(); + case CERTIFICATE: + this->process_certificate_msg(state, contents); + break; - if(sig_algo != "") - { - BOTAN_ASSERT(!cert_chains[sig_algo].empty(), - "Attempting to send empty certificate chain"); + case CLIENT_KEX: + this->process_client_key_exchange_msg(state, contents); + break; - state.server_certs(new Certificate(state.handshake_io(), - state.hash(), - cert_chains[sig_algo])); - } + case CERTIFICATE_VERIFY: + this->process_certificate_verify_msg(state, type, contents); + break; - Private_Key* private_key = nullptr; + case HANDSHAKE_CCS: + this->process_change_cipher_spec_msg(state); + break; - if(kex_algo == "RSA" || sig_algo != "") - { - private_key = m_creds.private_key_for( - state.server_certs()->cert_chain()[0], - "tls-server", - sni_hostname); + case FINISHED: + this->process_finished_msg(state, type, contents); + break; + + default: + throw Unexpected_Message("Unknown handshake message received"); + break; + } + } + +void Server::session_resume(Server_Handshake_State& pending_state, + bool have_session_ticket_key, + Session& session_info) + { + // Only offer a resuming client a new ticket if they didn't send one this time, + // ie, resumed via server-side resumption. TODO: also send one if expiring soon? + + const bool offer_new_session_ticket = + (pending_state.client_hello()->supports_session_ticket() && + pending_state.client_hello()->session_ticket().empty() && + have_session_ticket_key); + + Server_Hello::Handshake_Info hs_info(pending_state.handshake_io(), + pending_state.hash()); + pending_state.server_hello(new Server_Hello( + hs_info, + policy(), + rng(), + secure_renegotiation_data_for_server_hello(), + *pending_state.client_hello(), + session_info, + offer_new_session_ticket, + m_next_protocol + )); + + secure_renegotiation_check(pending_state.server_hello()); + + pending_state.compute_session_keys(session_info.master_secret()); + + if(!save_session(session_info)) + { + session_manager().remove_entry(session_info.session_id()); - if(!private_key) - throw Internal_Error("No private key located for associated server cert"); + if(pending_state.server_hello()->supports_session_ticket()) // send an empty ticket + { + pending_state.new_session_ticket( + new New_Session_Ticket(pending_state.handshake_io(), + pending_state.hash()) + ); } + } - if(kex_algo == "RSA") + if(pending_state.server_hello()->supports_session_ticket() && !pending_state.new_session_ticket()) + { + try { - state.server_rsa_kex_key = private_key; + const SymmetricKey ticket_key = m_creds.psk("tls-server", "session-ticket", ""); + + pending_state.new_session_ticket( + new New_Session_Ticket(pending_state.handshake_io(), + pending_state.hash(), + session_info.encrypt(ticket_key, rng()), + policy().session_ticket_lifetime()) + ); } - else + catch(...) {} + + if(!pending_state.new_session_ticket()) { - state.server_kex(new Server_Key_Exchange(state.handshake_io(), - state, policy(), - m_creds, rng(), private_key)); + pending_state.new_session_ticket( + new New_Session_Ticket(pending_state.handshake_io(), pending_state.hash()) + ); } + } - auto trusted_CAs = m_creds.trusted_certificate_authorities("tls-server", sni_hostname); + pending_state.handshake_io().send(Change_Cipher_Spec()); - std::vector client_auth_CAs; + change_cipher_spec_writer(SERVER); - for(auto store : trusted_CAs) - { - auto subjects = store->all_subjects(); - client_auth_CAs.insert(client_auth_CAs.end(), subjects.begin(), subjects.end()); - } + pending_state.server_finished(new Finished(pending_state.handshake_io(), pending_state, SERVER)); + pending_state.set_expected_next(HANDSHAKE_CCS); + } - if(!client_auth_CAs.empty() && state.ciphersuite().sig_algo() != "") - { - state.cert_req( - new Certificate_Req(state.handshake_io(), state.hash(), - policy(), client_auth_CAs, state.version())); +void Server::session_create(Server_Handshake_State& pending_state, + bool have_session_ticket_key) + { + std::map > cert_chains; - state.set_expected_next(CERTIFICATE); - } + const std::string sni_hostname = pending_state.client_hello()->sni_hostname(); - /* - * If the client doesn't have a cert they want to use they are - * allowed to send either an empty cert message or proceed - * directly to the client key exchange, so allow either case. - */ - state.set_expected_next(CLIENT_KEX); + cert_chains = get_server_certs(sni_hostname, m_creds); - state.server_hello_done(new Server_Hello_Done(state.handshake_io(), state.hash())); - } - } - else if(type == CERTIFICATE) + if(sni_hostname != "" && cert_chains.empty()) { - state.client_certs(new Certificate(contents)); + cert_chains = get_server_certs("", m_creds); - state.set_expected_next(CLIENT_KEX); + /* + * Only send the unrecognized_name alert if we couldn't + * find any certs for the requested name but did find at + * least one cert to use in general. That avoids sending an + * unrecognized_name when a server is configured for purely + * anonymous operation. + */ + if(!cert_chains.empty()) + send_alert(Alert(Alert::UNRECOGNIZED_NAME)); } - else if(type == CLIENT_KEX) - { - if(state.received_handshake_msg(CERTIFICATE) && !state.client_certs()->empty()) - state.set_expected_next(CERTIFICATE_VERIFY); - else - state.set_expected_next(HANDSHAKE_CCS); - - state.client_kex( - new Client_Key_Exchange(contents, state, - state.server_rsa_kex_key, - m_creds, policy(), rng()) - ); - state.compute_session_keys(); - } - else if(type == CERTIFICATE_VERIFY) + Server_Hello::Settings srv_settings( + make_hello_random(rng(), policy()), // new session ID + pending_state.version(), + choose_ciphersuite(policy(), + pending_state.version(), + m_creds, + cert_chains, + pending_state.client_hello()), + choose_compression(policy(), + pending_state.client_hello()->compression_methods()), + have_session_ticket_key); + + Server_Hello::Handshake_Info hs_info(pending_state.handshake_io(), + pending_state.hash()); + pending_state.server_hello(new Server_Hello( + hs_info, + policy(), + rng(), + secure_renegotiation_data_for_server_hello(), + *pending_state.client_hello(), + srv_settings, + m_next_protocol) + ); + + secure_renegotiation_check(pending_state.server_hello()); + + const std::string sig_algo = pending_state.ciphersuite().sig_algo(); + const std::string kex_algo = pending_state.ciphersuite().kex_algo(); + + if(sig_algo != "") { - state.client_verify(new Certificate_Verify(contents, state.version())); + BOTAN_ASSERT(!cert_chains[sig_algo].empty(), + "Attempting to send empty certificate chain"); - const std::vector& client_certs = - state.client_certs()->cert_chain(); + Certificate::Handshake_Info hs_info(pending_state.handshake_io(), + pending_state.hash()); - const bool sig_valid = - state.client_verify()->verify(client_certs[0], state, policy()); + pending_state.server_certs(new Certificate(hs_info, cert_chains[sig_algo])); + } - state.hash().update(state.handshake_io().format(contents, type)); + Private_Key* private_key = nullptr; - /* - * Using DECRYPT_ERROR looks weird here, but per RFC 4346 is for - * "A handshake cryptographic operation failed, including being - * unable to correctly verify a signature, ..." - */ - if(!sig_valid) - throw TLS_Exception(Alert::DECRYPT_ERROR, "Client cert verify failed"); - - try - { - m_creds.verify_certificate_chain("tls-server", "", client_certs); - } - catch(std::exception& e) - { - throw TLS_Exception(Alert::BAD_CERTIFICATE, e.what()); - } + if(kex_algo == "RSA" || sig_algo != "") + { + private_key = m_creds.private_key_for( + pending_state.server_certs()->cert_chain()[0], + "tls-server", + sni_hostname); - state.set_expected_next(HANDSHAKE_CCS); + if(!private_key) + throw Internal_Error("No private key located for associated server cert"); } - else if(type == HANDSHAKE_CCS) + + if(kex_algo == "RSA") { - state.set_expected_next(FINISHED); - change_cipher_spec_reader(SERVER); + pending_state.set_server_rsa_kex_key(private_key); } - else if(type == FINISHED) + else { - state.set_expected_next(HANDSHAKE_NONE); - - state.client_finished(new Finished(contents)); - if(!state.client_finished()->verify(state, CLIENT)) - throw TLS_Exception(Alert::DECRYPT_ERROR, - "Finished message didn't verify"); + pending_state.server_kex(new Server_Key_Exchange(pending_state.handshake_io(), + pending_state, policy(), + m_creds, rng(), private_key)); + } - if(!state.server_finished()) - { - // already sent finished if resuming, so this is a new session + auto trusted_CAs = m_creds.trusted_certificate_authorities("tls-server", sni_hostname); - state.hash().update(state.handshake_io().format(contents, type)); + std::vector client_auth_CAs; - Session session_info( - state.server_hello()->session_id(), - state.session_keys().master_secret(), - state.server_hello()->version(), - state.server_hello()->ciphersuite(), - state.server_hello()->compression_method(), - SERVER, - state.server_hello()->supports_extended_master_secret(), - get_peer_cert_chain(state), - std::vector(), - Server_Information(state.client_hello()->sni_hostname()), - state.srp_identifier(), - state.server_hello()->srtp_profile() - ); - - if(save_session(session_info)) - { - if(state.server_hello()->supports_session_ticket()) - { - try - { - const SymmetricKey ticket_key = m_creds.psk("tls-server", "session-ticket", ""); - - state.new_session_ticket( - new New_Session_Ticket(state.handshake_io(), - state.hash(), - session_info.encrypt(ticket_key, rng()), - policy().session_ticket_lifetime()) - ); - } - catch(...) {} - } - else - session_manager().save(session_info); - } - - if(!state.new_session_ticket() && - state.server_hello()->supports_session_ticket()) - { - state.new_session_ticket( - new New_Session_Ticket(state.handshake_io(), state.hash()) - ); - } + for(auto store : trusted_CAs) + { + auto subjects = store->all_subjects(); + client_auth_CAs.insert(client_auth_CAs.end(), subjects.begin(), subjects.end()); + } - state.handshake_io().send(Change_Cipher_Spec()); + if(!client_auth_CAs.empty() && pending_state.ciphersuite().sig_algo() != "") + { + Certificate_Req::Handshake_Info hs_info(pending_state.handshake_io(), + pending_state.hash()); + pending_state.cert_req( + new Certificate_Req(hs_info, policy(), client_auth_CAs, + pending_state.version())); - change_cipher_spec_writer(SERVER); + pending_state.set_expected_next(CERTIFICATE); + } - state.server_finished(new Finished(state.handshake_io(), state, SERVER)); - } + /* + * If the client doesn't have a cert they want to use they are + * allowed to send either an empty cert message or proceed + * directly to the client key exchange, so allow either case. + */ + pending_state.set_expected_next(CLIENT_KEX); - activate_session(); - } - else - throw Unexpected_Message("Unknown handshake message received"); + pending_state.server_hello_done(new Server_Hello_Done(pending_state.handshake_io(), pending_state.hash())); } - } } diff --git a/src/lib/tls/tls_server.h b/src/lib/tls/tls_server.h index 5ea2a1318..72834785e 100644 --- a/src/lib/tls/tls_server.h +++ b/src/lib/tls/tls_server.h @@ -1,6 +1,7 @@ /* * TLS Server * (C) 2004-2011 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -10,8 +11,11 @@ #include #include +#include #include + + namespace Botan { namespace TLS { @@ -27,32 +31,15 @@ class BOTAN_DLL Server final : public Channel /** * Server initialization */ - Server(output_fn output, - data_cb data_cb, - alert_cb alert_cb, - handshake_cb handshake_cb, + Server(const Callbacks& callbacks, Session_Manager& session_manager, Credentials_Manager& creds, const Policy& policy, RandomNumberGenerator& rng, next_protocol_fn next_proto = next_protocol_fn(), bool is_datagram = false, - size_t reserved_io_buffer_size = 16*1024 - ); - - Server(output_fn output, - data_cb data_cb, - alert_cb alert_cb, - handshake_cb handshake_cb, - handshake_msg_cb hs_msg_cb, - Session_Manager& session_manager, - Credentials_Manager& creds, - const Policy& policy, - RandomNumberGenerator& rng, - next_protocol_fn next_proto = next_protocol_fn(), - bool is_datagram = false + size_t reserved_io_buffer_size = TLS::Server::IO_BUF_DEFAULT_SIZE ); - /** * Return the protocol notification set by the client (using the * NPN extension) for this connection, if any. This value is not @@ -73,6 +60,33 @@ class BOTAN_DLL Server final : public Channel Handshake_Type type, const std::vector& contents) override; + void process_client_hello_msg(const Handshake_State* active_state, + Server_Handshake_State& pending_state, + const std::vector& contents); + + void process_certificate_msg(Server_Handshake_State& pending_state, + const std::vector& contents); + + void process_client_key_exchange_msg(Server_Handshake_State& pending_state, + const std::vector& contents); + + void process_change_cipher_spec_msg(Server_Handshake_State& pending_state); + + void process_certificate_verify_msg(Server_Handshake_State& pending_state, + Handshake_Type type, + const std::vector& contents); + + void process_finished_msg(Server_Handshake_State& pending_state, + Handshake_Type type, + const std::vector& contents); + + void session_resume(Server_Handshake_State& pending_state, + bool have_session_ticket_key, + Session& session_info); + + void session_create(Server_Handshake_State& pending_state, + bool have_session_ticket_key); + Handshake_State* new_handshake_state(Handshake_IO* io) override; Credentials_Manager& m_creds; diff --git a/src/lib/tls/tls_server_handshake_state.h b/src/lib/tls/tls_server_handshake_state.h new file mode 100644 index 000000000..281dd82df --- /dev/null +++ b/src/lib/tls/tls_server_handshake_state.h @@ -0,0 +1,47 @@ +/* +* TLS Server +* (C) 2004-2011,2012,2016 Jack Lloyd +* 2016 Matthias Gierlings +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_TLS_SERVER_HANDSHAKE_STATE_H__ +#define BOTAN_TLS_SERVER_HANDSHAKE_STATE_H__ + +#include +namespace Botan { + +namespace TLS { + +class Server_Handshake_State : public Handshake_State + { + public: + Server_Handshake_State(Handshake_IO* io, handshake_msg_cb cb) + : Handshake_State(io, cb) {} + + Private_Key* server_rsa_kex_key() { return m_server_rsa_kex_key; } + void set_server_rsa_kex_key(Private_Key* key) + { m_server_rsa_kex_key = key; } + + bool allow_session_resumption() const + { return m_allow_session_resumption; } + void set_allow_session_resumption(bool allow_session_resumption) + { m_allow_session_resumption = allow_session_resumption; } + + + private: + // Used by the server only, in case of RSA key exchange. Not owned + Private_Key* m_server_rsa_kex_key = nullptr; + + /* + * Used by the server to know if resumption should be allowed on + * a server-initiated renegotiation + */ + bool m_allow_session_resumption = true; + }; + +} + +} +#endif //BOTAN_TLS_SERVER_HANDSHAKE_STATE_H__ diff --git a/src/lib/tls/tls_session.cpp b/src/lib/tls/tls_session.cpp index 18c9b357c..bcbac10af 100644 --- a/src/lib/tls/tls_session.cpp +++ b/src/lib/tls/tls_session.cpp @@ -1,6 +1,7 @@ /* * TLS Session State * (C) 2011-2012,2015 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -19,29 +20,19 @@ namespace TLS { Session::Session(const std::vector& session_identifier, const secure_vector& master_secret, - Protocol_Version version, - u16bit ciphersuite, - byte compression_method, Connection_Side side, bool extended_master_secret, const std::vector& certs, const std::vector& ticket, - const Server_Information& server_info, - const std::string& srp_identifier, - u16bit srtp_profile) : + Properties properties) : m_start_time(std::chrono::system_clock::now()), m_identifier(session_identifier), m_session_ticket(ticket), m_master_secret(master_secret), - m_version(version), - m_ciphersuite(ciphersuite), - m_compression_method(compression_method), m_connection_side(side), - m_srtp_profile(srtp_profile), m_extended_master_secret(extended_master_secret), m_peer_certs(certs), - m_server_info(server_info), - m_srp_identifier(srp_identifier) + m_properties(properties) { } @@ -69,6 +60,9 @@ Session::Session(const byte ber[], size_t ber_len) size_t srtp_profile = 0; size_t fragment_size = 0; + u16bit cs = m_properties.get_ciphersuite(); + byte compr = compression_method(); + BER_Decoder(ber, ber_len) .start_cons(SEQUENCE) .decode_and_check(static_cast(TLS_SESSION_PARAM_STRUCT_VERSION), @@ -78,10 +72,9 @@ Session::Session(const byte ber[], size_t ber_len) .decode_integer_type(minor_version) .decode(m_identifier, OCTET_STRING) .decode(m_session_ticket, OCTET_STRING) - .decode_integer_type(m_ciphersuite) - .decode_integer_type(m_compression_method) + .decode_integer_type(cs) + .decode_integer_type(compr) .decode_integer_type(side_code) - .decode_integer_type(fragment_size) .decode(m_extended_master_secret) .decode(m_master_secret, OCTET_STRING) .decode(peer_cert_bits, OCTET_STRING) @@ -103,16 +96,17 @@ Session::Session(const byte ber[], size_t ber_len) " no longer supported"); } - m_version = Protocol_Version(major_version, minor_version); + m_properties.set_ciphersuite(cs); + m_properties.set_compression_method(compr); + m_properties.set_protocol_version(Protocol_Version(major_version, minor_version)); m_start_time = std::chrono::system_clock::from_time_t(start_time); m_connection_side = static_cast(side_code); - m_srtp_profile = static_cast(srtp_profile); - - m_server_info = Server_Information(server_hostname.value(), - server_service.value(), - static_cast(server_port)); - - m_srp_identifier = srp_identifier_str.value(); + m_properties.set_srtp_profile(static_cast(srtp_profile)); + m_properties.set_server_info( + Server_Information(server_hostname.value(), + server_service.value(), + static_cast(server_port))); + m_properties.set_srp_identifier(srp_identifier_str.value()); if(!peer_cert_bits.empty()) { @@ -133,22 +127,22 @@ secure_vector Session::DER_encode() const .start_cons(SEQUENCE) .encode(static_cast(TLS_SESSION_PARAM_STRUCT_VERSION)) .encode(static_cast(std::chrono::system_clock::to_time_t(m_start_time))) - .encode(static_cast(m_version.major_version())) - .encode(static_cast(m_version.minor_version())) + .encode(static_cast(version().major_version())) + .encode(static_cast(version().minor_version())) .encode(m_identifier, OCTET_STRING) .encode(m_session_ticket, OCTET_STRING) - .encode(static_cast(m_ciphersuite)) - .encode(static_cast(m_compression_method)) + .encode(static_cast(ciphersuite_code())) + .encode(static_cast(compression_method())) .encode(static_cast(m_connection_side)) .encode(static_cast(/*old fragment size*/0)) .encode(m_extended_master_secret) .encode(m_master_secret, OCTET_STRING) .encode(peer_cert_bits, OCTET_STRING) - .encode(ASN1_String(m_server_info.hostname(), UTF8_STRING)) - .encode(ASN1_String(m_server_info.service(), UTF8_STRING)) - .encode(static_cast(m_server_info.port())) - .encode(ASN1_String(m_srp_identifier, UTF8_STRING)) - .encode(static_cast(m_srtp_profile)) + .encode(ASN1_String(m_properties.get_server_info().hostname(), UTF8_STRING)) + .encode(ASN1_String(m_properties.get_server_info().service(), UTF8_STRING)) + .encode(static_cast(m_properties.get_server_info().port())) + .encode(ASN1_String(srp_identifier(), UTF8_STRING)) + .encode(static_cast(dtls_srtp_profile())) .end_cons() .get_contents(); } diff --git a/src/lib/tls/tls_session.h b/src/lib/tls/tls_session.h index 8ca646cf2..600aa0a10 100644 --- a/src/lib/tls/tls_session.h +++ b/src/lib/tls/tls_session.h @@ -1,6 +1,7 @@ /* * TLS Session * (C) 2011-2012,2015 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -27,35 +28,106 @@ namespace TLS { class BOTAN_DLL Session { public: + class Properties + { + public: + Properties() : m_srtp_profile(0), m_protocol_version(), + m_ciphersuite(), m_compression_method(0) {} + + Properties(const Server_Information& server_info, + const std::string& srp_identifier, + u16bit srtp_profile, + Protocol_Version protocol_version, + u16bit ciphersuite, + byte compression_method) + : m_server_info(server_info), + m_srp_identifier(srp_identifier), + m_srtp_profile(srtp_profile), + m_protocol_version(protocol_version), + m_ciphersuite(ciphersuite), + m_compression_method(compression_method) {} + + const Server_Information& get_server_info() const + { + return m_server_info; + } + + void set_server_info(Server_Information server_info) + { + m_server_info = server_info; + } + + const std::string& get_srp_identifier() const + { + return m_srp_identifier; + } + + void set_srp_identifier(const std::string& srp_identifier) + { + m_srp_identifier = srp_identifier; + } + + u16bit get_srtp_profile() const { return m_srtp_profile; } + void set_srtp_profile(u16bit srtp_profile) + { + m_srtp_profile = srtp_profile; + } + + Protocol_Version get_protocol_version() const + { + return m_protocol_version; + } + + void set_protocol_version(Protocol_Version protocol_version) + { + m_protocol_version = protocol_version; + } + + u16bit get_ciphersuite() const { return m_ciphersuite; } + + void set_ciphersuite(u16bit ciphersuite) + { + m_ciphersuite = ciphersuite; + } + + byte get_compression_method() const + { + return m_compression_method; + } + + void set_compression_method(byte compression_method) + { + m_compression_method = compression_method; + } + + private: + Server_Information m_server_info; + std::string m_srp_identifier; + u16bit m_srtp_profile; + Protocol_Version m_protocol_version; + u16bit m_ciphersuite; + byte m_compression_method; + }; /** * Uninitialized session */ Session() : m_start_time(std::chrono::system_clock::time_point::min()), - m_version(), - m_ciphersuite(0), - m_compression_method(0), m_connection_side(static_cast(0)), - m_srtp_profile(0), - m_extended_master_secret(false) - {} + m_extended_master_secret(false), + m_properties() {} /** * New session (sets session start time) */ Session(const std::vector& session_id, const secure_vector& master_secret, - Protocol_Version version, - u16bit ciphersuite, - byte compression_method, Connection_Side side, bool supports_extended_master_secret, const std::vector& peer_certs, const std::vector& session_ticket, - const Server_Information& server_info, - const std::string& srp_identifier, - u16bit srtp_profile); + Properties properties); /** * Load a session from DER representation (created by DER_encode) @@ -112,22 +184,22 @@ class BOTAN_DLL Session /** * Get the version of the saved session */ - Protocol_Version version() const { return m_version; } + Protocol_Version version() const { return m_properties.get_protocol_version(); } /** * Get the ciphersuite code of the saved session */ - u16bit ciphersuite_code() const { return m_ciphersuite; } + u16bit ciphersuite_code() const { return m_properties.get_ciphersuite(); } /** * Get the ciphersuite info of the saved session */ - Ciphersuite ciphersuite() const { return Ciphersuite::by_id(m_ciphersuite); } + Ciphersuite ciphersuite() const { return Ciphersuite::by_id(ciphersuite_code()); } /** * Get the compression method used in the saved session */ - byte compression_method() const { return m_compression_method; } + byte compression_method() const { return m_properties.get_compression_method(); } /** * Get which side of the connection the resumed session we are/were @@ -138,7 +210,7 @@ class BOTAN_DLL Session /** * Get the SRP identity (if sent by the client in the initial handshake) */ - const std::string& srp_identifier() const { return m_srp_identifier; } + const std::string& srp_identifier() const { return m_properties.get_srp_identifier(); } /** * Get the saved master secret @@ -153,7 +225,7 @@ class BOTAN_DLL Session /** * Get the negotiated DTLS-SRTP algorithm (RFC 5764) */ - u16bit dtls_srtp_profile() const { return m_srtp_profile; } + u16bit dtls_srtp_profile() const { return m_properties.get_srtp_profile(); } bool supports_extended_master_secret() const { return m_extended_master_secret; } @@ -177,7 +249,7 @@ class BOTAN_DLL Session */ const std::vector& session_ticket() const { return m_session_ticket; } - const Server_Information& server_info() const { return m_server_info; } + const Server_Information& server_info() const { return m_properties.get_server_info(); } private: enum { TLS_SESSION_PARAM_STRUCT_VERSION = 20160103 }; @@ -188,16 +260,10 @@ class BOTAN_DLL Session std::vector m_session_ticket; // only used by client side secure_vector m_master_secret; - Protocol_Version m_version; - u16bit m_ciphersuite; - byte m_compression_method; Connection_Side m_connection_side; - u16bit m_srtp_profile; bool m_extended_master_secret; - std::vector m_peer_certs; - Server_Information m_server_info; // optional - std::string m_srp_identifier; // optional + Properties m_properties; }; } diff --git a/src/tests/unit_tls.cpp b/src/tests/unit_tls.cpp index f125bfcb5..6e6ac29a0 100644 --- a/src/tests/unit_tls.cpp +++ b/src/tests/unit_tls.cpp @@ -1,5 +1,6 @@ /* * (C) 2014,2015 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -218,10 +219,12 @@ Test::Result test_tls_handshake(Botan::TLS::Protocol_Version offer_version, { std::vector c2s_traffic, s2c_traffic, client_recv, server_recv, client_sent, server_sent; - Botan::TLS::Server server(queue_inserter(s2c_traffic), - queue_inserter(server_recv), - print_alert, - handshake_complete, + + Botan::TLS::Server server(Botan::TLS::Server::Callbacks( + queue_inserter(s2c_traffic), + queue_inserter(server_recv), + print_alert, + handshake_complete), server_sessions, creds, policy, @@ -229,17 +232,19 @@ Test::Result test_tls_handshake(Botan::TLS::Protocol_Version offer_version, next_protocol_chooser, false); - Botan::TLS::Client client(queue_inserter(c2s_traffic), - queue_inserter(client_recv), - print_alert, - handshake_complete, + Botan::TLS::Client::Callbacks client_callbacks(queue_inserter(c2s_traffic), + queue_inserter(client_recv), + print_alert, + handshake_complete); + Botan::TLS::Client client(client_callbacks, client_sessions, creds, policy, rng, - Botan::TLS::Server_Information("server.example.com"), - offer_version, - protocols_offered); + Botan::TLS::Client::Properties( + Botan::TLS::Server_Information("server.example.com"), + offer_version, + protocols_offered)); size_t rounds = 0; @@ -444,10 +449,11 @@ Test::Result test_dtls_handshake(Botan::TLS::Protocol_Version offer_version, { std::vector c2s_traffic, s2c_traffic, client_recv, server_recv, client_sent, server_sent; - Botan::TLS::Server server(queue_inserter(s2c_traffic), - queue_inserter(server_recv), - print_alert, - handshake_complete, + Botan::TLS::Server::Callbacks server_callbacks(queue_inserter(s2c_traffic), + queue_inserter(server_recv), + print_alert, + handshake_complete); + Botan::TLS::Server server(server_callbacks, server_sessions, creds, policy, @@ -455,17 +461,19 @@ Test::Result test_dtls_handshake(Botan::TLS::Protocol_Version offer_version, next_protocol_chooser, true); - Botan::TLS::Client client(queue_inserter(c2s_traffic), - queue_inserter(client_recv), - print_alert, - handshake_complete, + Botan::TLS::Client::Callbacks client_callbacks(queue_inserter(c2s_traffic), + queue_inserter(client_recv), + print_alert, + handshake_complete); + Botan::TLS::Client client(client_callbacks, client_sessions, creds, policy, rng, - Botan::TLS::Server_Information("server.example.com"), - offer_version, - protocols_offered); + Botan::TLS::Client::Properties( + Botan::TLS::Server_Information("server.example.com"), + offer_version, + protocols_offered)); size_t rounds = 0; -- cgit v1.2.3 From 8350d1e081dc4c2330f4c7a35a746b7682d7f0c1 Mon Sep 17 00:00:00 2001 From: Matthias Gierlings Date: Fri, 29 Apr 2016 20:45:47 +0200 Subject: Reduction of code complexity in MP & ECC classes. - reduced number of parameters in various methods - introduced structures and renamed variables to improve code readability. --- src/lib/cert/x509/x509_ca.cpp | 1 + src/lib/cert/x509/x509_ca.h | 1 + src/lib/cert/x509/x509self.cpp | 1 + src/lib/math/bigint/big_ops2.cpp | 6 +-- src/lib/math/bigint/big_ops3.cpp | 5 +-- src/lib/math/ec_gfp/curve_gfp.cpp | 19 +++------ src/lib/math/mp/mp_core.h | 13 +++--- src/lib/math/mp/mp_karat.cpp | 58 ++++++++++++-------------- src/lib/math/mp/mp_monty.cpp | 25 +++++------ src/lib/math/numbertheory/mp_numth.cpp | 16 +++---- src/lib/math/numbertheory/powm_mnt.cpp | 28 ++++--------- src/lib/pubkey/curve25519/donna.cpp | 76 ++++++++++++++++++++-------------- 12 files changed, 114 insertions(+), 135 deletions(-) (limited to 'src/lib/cert') diff --git a/src/lib/cert/x509/x509_ca.cpp b/src/lib/cert/x509/x509_ca.cpp index 3f7af77f5..02f77c8f6 100644 --- a/src/lib/cert/x509/x509_ca.cpp +++ b/src/lib/cert/x509/x509_ca.cpp @@ -1,6 +1,7 @@ /* * X.509 Certificate Authority * (C) 1999-2010 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ diff --git a/src/lib/cert/x509/x509_ca.h b/src/lib/cert/x509/x509_ca.h index 8cedb9db9..17e534cfd 100644 --- a/src/lib/cert/x509/x509_ca.h +++ b/src/lib/cert/x509/x509_ca.h @@ -1,6 +1,7 @@ /* * X.509 Certificate Authority * (C) 1999-2008 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ diff --git a/src/lib/cert/x509/x509self.cpp b/src/lib/cert/x509/x509self.cpp index 62f9fc370..636b9fbb6 100644 --- a/src/lib/cert/x509/x509self.cpp +++ b/src/lib/cert/x509/x509self.cpp @@ -1,6 +1,7 @@ /* * PKCS #10/Self Signed Cert Creation * (C) 1999-2008 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ diff --git a/src/lib/math/bigint/big_ops2.cpp b/src/lib/math/bigint/big_ops2.cpp index 9a3408247..6e234f036 100644 --- a/src/lib/math/bigint/big_ops2.cpp +++ b/src/lib/math/bigint/big_ops2.cpp @@ -1,6 +1,7 @@ /* * BigInt Assignment Operators * (C) 1999-2007 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -118,10 +119,7 @@ BigInt& BigInt::operator*=(const BigInt& y) secure_vector z(data(), data() + x_sw); secure_vector workspace(size()); - - bigint_mul(mutable_data(), size(), workspace.data(), - z.data(), z.size(), x_sw, - y.data(), y.size(), y_sw); + bigint_mul(*this, BigInt(*this), y, workspace.data()); } return (*this); diff --git a/src/lib/math/bigint/big_ops3.cpp b/src/lib/math/bigint/big_ops3.cpp index 6cf837020..24927b4fc 100644 --- a/src/lib/math/bigint/big_ops3.cpp +++ b/src/lib/math/bigint/big_ops3.cpp @@ -1,6 +1,7 @@ /* * BigInt Binary Operators * (C) 1999-2007 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -93,9 +94,7 @@ BigInt operator*(const BigInt& x, const BigInt& y) else if(x_sw && y_sw) { secure_vector workspace(z.size()); - bigint_mul(z.mutable_data(), z.size(), workspace.data(), - x.data(), x.size(), x_sw, - y.data(), y.size(), y_sw); + bigint_mul(z, x, y, workspace.data()); } if(x_sw && y_sw && x.sign() != y.sign()) diff --git a/src/lib/math/ec_gfp/curve_gfp.cpp b/src/lib/math/ec_gfp/curve_gfp.cpp index 9bf2191c6..96593e601 100644 --- a/src/lib/math/ec_gfp/curve_gfp.cpp +++ b/src/lib/math/ec_gfp/curve_gfp.cpp @@ -1,6 +1,7 @@ /* * Elliptic curves over GF(p) Montgomery Representation * (C) 2014,2015 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -80,20 +81,14 @@ void CurveGFp_Montgomery::curve_mul(BigInt& z, const BigInt& x, const BigInt& y, return; } - const size_t x_sw = x.sig_words(); - const size_t y_sw = y.sig_words(); - const size_t output_size = 2*m_p_words + 1; ws.resize(2*(m_p_words+2)); z.grow_to(output_size); z.clear(); - bigint_monty_mul(z.mutable_data(), output_size, - x.data(), x.size(), x_sw, - y.data(), y.size(), y_sw, - m_p.data(), m_p_words, m_p_dash, - ws.data()); + bigint_monty_mul(z, x, y, m_p.data(), m_p_words, m_p_dash, ws.data()); + } void CurveGFp_Montgomery::curve_sqr(BigInt& z, const BigInt& x, @@ -115,9 +110,7 @@ void CurveGFp_Montgomery::curve_sqr(BigInt& z, const BigInt& x, z.grow_to(output_size); z.clear(); - bigint_monty_sqr(z.mutable_data(), output_size, - x.data(), x.size(), x_sw, - m_p.data(), m_p_words, m_p_dash, + bigint_monty_sqr(z, x, m_p.data(), m_p_words, m_p_dash, ws.data()); } @@ -174,9 +167,7 @@ void CurveGFp_NIST::curve_mul(BigInt& z, const BigInt& x, const BigInt& y, z.grow_to(output_size); z.clear(); - bigint_mul(z.mutable_data(), output_size, ws.data(), - x.data(), x.size(), x.sig_words(), - y.data(), y.size(), y.sig_words()); + bigint_mul(z, x, y, ws.data()); this->redc(z, ws); } diff --git a/src/lib/math/mp/mp_core.h b/src/lib/math/mp/mp_core.h index 73f13742c..c4ce005ba 100644 --- a/src/lib/math/mp/mp_core.h +++ b/src/lib/math/mp/mp_core.h @@ -2,6 +2,7 @@ * MPI Algorithms * (C) 1999-2010 Jack Lloyd * 2006 Luca Piccarreta +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -9,6 +10,7 @@ #ifndef BOTAN_MP_CORE_OPS_H__ #define BOTAN_MP_CORE_OPS_H__ +#include #include namespace Botan { @@ -134,17 +136,14 @@ void bigint_monty_redc(word z[], /* * Montgomery Multiplication */ -void bigint_monty_mul(word z[], size_t z_size, - const word x[], size_t x_size, size_t x_sw, - const word y[], size_t y_size, size_t y_sw, +void bigint_monty_mul(BigInt& z, const BigInt& x, const BigInt& y, const word p[], size_t p_size, word p_dash, word workspace[]); /* * Montgomery Squaring */ -void bigint_monty_sqr(word z[], size_t z_size, - const word x[], size_t x_size, size_t x_sw, +void bigint_monty_sqr(BigInt& z, const BigInt& x, const word p[], size_t p_size, word p_dash, word workspace[]); @@ -182,9 +181,7 @@ void bigint_comba_sqr16(word out[32], const word in[16]); /* * High Level Multiplication/Squaring Interfaces */ -void bigint_mul(word z[], size_t z_size, word workspace[], - const word x[], size_t x_size, size_t x_sw, - const word y[], size_t y_size, size_t y_sw); +void bigint_mul(BigInt& z, const BigInt& x, const BigInt& y, word workspace[]); void bigint_sqr(word z[], size_t z_size, word workspace[], const word x[], size_t x_size, size_t x_sw); diff --git a/src/lib/math/mp/mp_karat.cpp b/src/lib/math/mp/mp_karat.cpp index 9135fdd6a..7a763e2a9 100644 --- a/src/lib/math/mp/mp_karat.cpp +++ b/src/lib/math/mp/mp_karat.cpp @@ -1,6 +1,7 @@ /* * Multiplication and Squaring * (C) 1999-2010 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -252,60 +253,55 @@ size_t karatsuba_size(size_t z_size, size_t x_size, size_t x_sw) /* * Multiplication Algorithm Dispatcher */ -void bigint_mul(word z[], size_t z_size, word workspace[], - const word x[], size_t x_size, size_t x_sw, - const word y[], size_t y_size, size_t y_sw) +void bigint_mul(BigInt& z, const BigInt& x, const BigInt& y, word workspace[]) { - // checking that z_size >= x_sw + y_sw without overflow - BOTAN_ASSERT(z_size > x_sw && z_size > y_sw && z_size-x_sw >= y_sw, "Output size is sufficient"); - - if(x_sw == 1) + if(x.sig_words() == 1) { - bigint_linmul3(z, y, y_sw, x[0]); + bigint_linmul3(z.mutable_data(), y.data(), y.sig_words(), x.data()[0]); } - else if(y_sw == 1) + else if(y.sig_words() == 1) { - bigint_linmul3(z, x, x_sw, y[0]); + bigint_linmul3(z.mutable_data(), x.data(), x.sig_words(), y.data()[0]); } - else if(x_sw <= 4 && x_size >= 4 && - y_sw <= 4 && y_size >= 4 && z_size >= 8) + else if(x.sig_words() <= 4 && x.size() >= 4 && + y.sig_words() <= 4 && y.size() >= 4 && z.size() >= 8) { - bigint_comba_mul4(z, x, y); + bigint_comba_mul4(z.mutable_data(), x.data(), y.data()); } - else if(x_sw <= 6 && x_size >= 6 && - y_sw <= 6 && y_size >= 6 && z_size >= 12) + else if(x.sig_words() <= 6 && x.size() >= 6 && + y.sig_words() <= 6 && y.size() >= 6 && z.size() >= 12) { - bigint_comba_mul6(z, x, y); + bigint_comba_mul6(z.mutable_data(), x.data(), y.data()); } - else if(x_sw <= 8 && x_size >= 8 && - y_sw <= 8 && y_size >= 8 && z_size >= 16) + else if(x.sig_words() <= 8 && x.size() >= 8 && + y.sig_words() <= 8 && y.size() >= 8 && z.size() >= 16) { - bigint_comba_mul8(z, x, y); + bigint_comba_mul8(z.mutable_data(), x.data(), y.data()); } - else if(x_sw <= 9 && x_size >= 9 && - y_sw <= 9 && y_size >= 9 && z_size >= 18) + else if(x.sig_words() <= 9 && x.size() >= 9 && + y.sig_words() <= 9 && y.size() >= 9 && z.size() >= 18) { - bigint_comba_mul9(z, x, y); + bigint_comba_mul9(z.mutable_data(), x.data(), y.data()); } - else if(x_sw <= 16 && x_size >= 16 && - y_sw <= 16 && y_size >= 16 && z_size >= 32) + else if(x.sig_words() <= 16 && x.size() >= 16 && + y.sig_words() <= 16 && y.size() >= 16 && z.size() >= 32) { - bigint_comba_mul16(z, x, y); + bigint_comba_mul16(z.mutable_data(), x.data(), y.data()); } - else if(x_sw < KARATSUBA_MULTIPLY_THRESHOLD || - y_sw < KARATSUBA_MULTIPLY_THRESHOLD || + else if(x.sig_words() < KARATSUBA_MULTIPLY_THRESHOLD || + y.sig_words() < KARATSUBA_MULTIPLY_THRESHOLD || !workspace) { - basecase_mul(z, x, x_sw, y, y_sw); + basecase_mul(z.mutable_data(), x.data(), x.sig_words(), y.data(), y.sig_words()); } else { - const size_t N = karatsuba_size(z_size, x_size, x_sw, y_size, y_sw); + const size_t N = karatsuba_size(z.size(), x.size(), x.sig_words(), y.size(), y.sig_words()); if(N) - karatsuba_mul(z, x, y, N, workspace); + karatsuba_mul(z.mutable_data(), x.data(), y.data(), N, workspace); else - basecase_mul(z, x, x_sw, y, y_sw); + basecase_mul(z.mutable_data(), x.data(), x.sig_words(), y.data(), y.sig_words()); } } diff --git a/src/lib/math/mp/mp_monty.cpp b/src/lib/math/mp/mp_monty.cpp index 7e427b540..88b5de715 100644 --- a/src/lib/math/mp/mp_monty.cpp +++ b/src/lib/math/mp/mp_monty.cpp @@ -2,10 +2,12 @@ * Montgomery Reduction * (C) 1999-2011 Jack Lloyd * 2006 Luca Piccarreta +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ +#include #include #include #include @@ -92,30 +94,25 @@ void bigint_monty_redc(word z[], BOTAN_ASSERT(borrow == 0 || borrow == 1, "Expected borrow"); } -void bigint_monty_mul(word z[], size_t z_size, - const word x[], size_t x_size, size_t x_sw, - const word y[], size_t y_size, size_t y_sw, +void bigint_monty_mul(BigInt& z, const BigInt& x, const BigInt& y, const word p[], size_t p_size, word p_dash, word ws[]) { - bigint_mul(&z[0], z_size, &ws[0], - &x[0], x_size, x_sw, - &y[0], y_size, y_sw); + bigint_mul(z, x, y, &ws[0]); - bigint_monty_redc(&z[0], + bigint_monty_redc(z.mutable_data(), &p[0], p_size, p_dash, &ws[0]); + } -void bigint_monty_sqr(word z[], size_t z_size, - const word x[], size_t x_size, size_t x_sw, - const word p[], size_t p_size, word p_dash, - word ws[]) +void bigint_monty_sqr(BigInt& z, const BigInt& x, const word p[], + size_t p_size, word p_dash, word ws[]) { - bigint_sqr(&z[0], z_size, &ws[0], - &x[0], x_size, x_sw); + bigint_sqr(z.mutable_data(), z.size(), &ws[0], + x.data(), x.size(), x.sig_words()); - bigint_monty_redc(&z[0], + bigint_monty_redc(z.mutable_data(), &p[0], p_size, p_dash, &ws[0]); } diff --git a/src/lib/math/numbertheory/mp_numth.cpp b/src/lib/math/numbertheory/mp_numth.cpp index 3373b9ee7..d78d21128 100644 --- a/src/lib/math/numbertheory/mp_numth.cpp +++ b/src/lib/math/numbertheory/mp_numth.cpp @@ -1,6 +1,7 @@ /* * Fused and Important MP Algorithms * (C) 1999-2007 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -40,20 +41,13 @@ BigInt mul_add(const BigInt& a, const BigInt& b, const BigInt& c) if(a.sign() != b.sign()) sign = BigInt::Negative; - const size_t a_sw = a.sig_words(); - const size_t b_sw = b.sig_words(); - const size_t c_sw = c.sig_words(); - - BigInt r(sign, std::max(a.size() + b.size(), c_sw) + 1); + BigInt r(sign, std::max(a.size() + b.size(), c.sig_words()) + 1); secure_vector workspace(r.size()); - bigint_mul(r.mutable_data(), r.size(), - workspace.data(), - a.data(), a.size(), a_sw, - b.data(), b.size(), b_sw); + bigint_mul(r, a, b, workspace.data()); - const size_t r_size = std::max(r.sig_words(), c_sw); - bigint_add2(r.mutable_data(), r_size, c.data(), c_sw); + const size_t r_size = std::max(r.sig_words(), c.sig_words()); + bigint_add2(r.mutable_data(), r_size, c.data(), c.sig_words()); return r; } diff --git a/src/lib/math/numbertheory/powm_mnt.cpp b/src/lib/math/numbertheory/powm_mnt.cpp index 5c441db3a..572f0de98 100644 --- a/src/lib/math/numbertheory/powm_mnt.cpp +++ b/src/lib/math/numbertheory/powm_mnt.cpp @@ -1,6 +1,7 @@ /* * Montgomery Exponentiation * (C) 1999-2010,2012 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -8,6 +9,7 @@ #include #include #include +#include namespace Botan { @@ -34,36 +36,26 @@ void Montgomery_Exponentiator::set_base(const BigInt& base) m_g[0] = 1; - bigint_monty_mul(z.mutable_data(), z.size(), - m_g[0].data(), m_g[0].size(), m_g[0].sig_words(), - m_R2_mod.data(), m_R2_mod.size(), m_R2_mod.sig_words(), + bigint_monty_mul(z, m_g[0], m_R2_mod, m_modulus.data(), m_mod_words, m_mod_prime, workspace.data()); - m_g[0] = z; m_g[1] = (base >= m_modulus) ? (base % m_modulus) : base; - bigint_monty_mul(z.mutable_data(), z.size(), - m_g[1].data(), m_g[1].size(), m_g[1].sig_words(), - m_R2_mod.data(), m_R2_mod.size(), m_R2_mod.sig_words(), + bigint_monty_mul(z, m_g[1], m_R2_mod, m_modulus.data(), m_mod_words, m_mod_prime, workspace.data()); m_g[1] = z; const BigInt& x = m_g[1]; - const size_t x_sig = x.sig_words(); for(size_t i = 2; i != m_g.size(); ++i) { const BigInt& y = m_g[i-1]; - const size_t y_sig = y.sig_words(); - bigint_monty_mul(z.mutable_data(), z.size(), - x.data(), x.size(), x_sig, - y.data(), y.size(), y_sig, - m_modulus.data(), m_mod_words, m_mod_prime, + bigint_monty_mul(z, x, y, m_modulus.data(), m_mod_words, m_mod_prime, workspace.data()); m_g[i] = z; @@ -82,15 +74,13 @@ BigInt Montgomery_Exponentiator::execute() const const size_t z_size = 2*(m_mod_words + 1); BigInt z(BigInt::Positive, z_size); - secure_vector workspace(z_size); + secure_vector workspace(z.size()); for(size_t i = exp_nibbles; i > 0; --i) { for(size_t k = 0; k != m_window_bits; ++k) { - bigint_monty_sqr(z.mutable_data(), z_size, - x.data(), x.size(), x.sig_words(), - m_modulus.data(), m_mod_words, m_mod_prime, + bigint_monty_sqr(z, x, m_modulus.data(), m_mod_words, m_mod_prime, workspace.data()); x = z; @@ -100,9 +90,7 @@ BigInt Montgomery_Exponentiator::execute() const const BigInt& y = m_g[nibble]; - bigint_monty_mul(z.mutable_data(), z_size, - x.data(), x.size(), x.sig_words(), - y.data(), y.size(), y.sig_words(), + bigint_monty_mul(z, x, y, m_modulus.data(), m_mod_words, m_mod_prime, workspace.data()); diff --git a/src/lib/pubkey/curve25519/donna.cpp b/src/lib/pubkey/curve25519/donna.cpp index 9b28e412c..a0e4d249f 100644 --- a/src/lib/pubkey/curve25519/donna.cpp +++ b/src/lib/pubkey/curve25519/donna.cpp @@ -39,6 +39,26 @@ typedef byte u8; typedef u64bit limb; typedef limb felem[5]; +typedef struct + { + limb* x; + limb* z; + } fmonty_pair_t; + +typedef struct + { + fmonty_pair_t q; + fmonty_pair_t q_dash; + const limb* q_minus_q_dash; + } fmonty_in_t; + +typedef struct + { + fmonty_pair_t two_q; + fmonty_pair_t q_plus_q_dash; + } fmonty_out_t; + + #if !defined(BOTAN_TARGET_HAS_NATIVE_UINT128) typedef donna128 uint128_t; #endif @@ -273,44 +293,41 @@ fcontract(u8 *output, const felem input) { /* Input: Q, Q', Q-Q' * Output: 2Q, Q+Q' * - * x2 z3: long form - * x3 z3: long form - * x z: short form, destroyed - * xprime zprime: short form, destroyed - * qmqp: short form, preserved + * result.two_q (2*Q): long form + * result.q_plus_q_dash (Q + Q): long form + * in.q: short form, destroyed + * in.q_dash: short form, destroyed + * in.q_minus_q_dash: short form, preserved */ static void -fmonty(limb *x2, limb *z2, /* output 2Q */ - limb *x3, limb *z3, /* output Q + Q' */ - limb *x, limb *z, /* input Q */ - limb *xprime, limb *zprime, /* input Q' */ - const limb *qmqp /* input Q - Q' */) { +fmonty(fmonty_out_t& result, fmonty_in_t& in) +{ limb origx[5], origxprime[5], zzz[5], xx[5], zz[5], xxprime[5], - zzprime[5], zzzprime[5]; + zzprime[5], zzzprime[5]; - copy_mem(origx, x, 5); - fsum(x, z); - fdifference_backwards(z, origx); // does x - z + copy_mem(origx, in.q.x, 5); + fsum(in.q.x, in.q.z); + fdifference_backwards(in.q.z, origx); // does x - z - copy_mem(origxprime, xprime, 5); - fsum(xprime, zprime); - fdifference_backwards(zprime, origxprime); - fmul(xxprime, xprime, z); - fmul(zzprime, x, zprime); + copy_mem(origxprime, in.q_dash.x, 5); + fsum(in.q_dash.x, in.q_dash.z); + fdifference_backwards(in.q_dash.z, origxprime); + fmul(xxprime, in.q_dash.x, in.q.z); + fmul(zzprime, in.q.x, in.q_dash.z); copy_mem(origxprime, xxprime, 5); fsum(xxprime, zzprime); fdifference_backwards(zzprime, origxprime); - fsquare_times(x3, xxprime, 1); + fsquare_times(result.q_plus_q_dash.x, xxprime, 1); fsquare_times(zzzprime, zzprime, 1); - fmul(z3, zzzprime, qmqp); + fmul(result.q_plus_q_dash.z, zzzprime, in.q_minus_q_dash); - fsquare_times(xx, x, 1); - fsquare_times(zz, z, 1); - fmul(x2, xx, zz); + fsquare_times(xx, in.q.x, 1); + fsquare_times(zz, in.q.z, 1); + fmul(result.two_q.x, xx, zz); fdifference_backwards(zz, xx); // does zz = xx - zz fscalar_product(zzz, zz, 121665); fsum(zzz, xx); - fmul(z2, zz, zzz); + fmul(result.two_q.z, zz, zzz); } // ----------------------------------------------------------------------------- @@ -356,11 +373,10 @@ cmult(limb *resultx, limb *resultz, const u8 *n, const limb *q) { swap_conditional(nqx, nqpqx, bit); swap_conditional(nqz, nqpqz, bit); - fmonty(nqx2, nqz2, - nqpqx2, nqpqz2, - nqx, nqz, - nqpqx, nqpqz, - q); + + fmonty_out_t result { nqx2, nqz2, nqpqx2, nqpqz2 }; + fmonty_in_t in { nqx, nqz, nqpqx, nqpqz, q }; + fmonty(result, in); swap_conditional(nqx2, nqpqx2, bit); swap_conditional(nqz2, nqpqz2, bit); -- cgit v1.2.3 From 129324f68f59bea91b3b8901875eeb278acb34b1 Mon Sep 17 00:00:00 2001 From: Matthias Gierlings Date: Sun, 19 Jun 2016 18:06:12 +0200 Subject: Reverted proposed constructor changes to X509_CA. - Removed Certificate_Properties class used to wrap X509_CA parameters. - Whitespace cleanup. --- src/lib/cert/x509/x509_ca.cpp | 20 ++++++++++---------- src/lib/cert/x509/x509_ca.h | 26 ++++---------------------- src/lib/cert/x509/x509self.cpp | 12 +++--------- src/lib/tls/tls_ciphersuite.h | 38 ++++++++++++++++++++++++++------------ src/lib/tls/tls_client.h | 2 +- src/lib/tls/tls_record.cpp | 2 +- 6 files changed, 45 insertions(+), 55 deletions(-) (limited to 'src/lib/cert') diff --git a/src/lib/cert/x509/x509_ca.cpp b/src/lib/cert/x509/x509_ca.cpp index 02f77c8f6..147fdd6ad 100644 --- a/src/lib/cert/x509/x509_ca.cpp +++ b/src/lib/cert/x509/x509_ca.cpp @@ -1,7 +1,6 @@ /* * X.509 Certificate Authority * (C) 1999-2010 Jack Lloyd -* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -79,10 +78,8 @@ X509_Certificate X509_CA::sign_request(const PKCS10_Request& req, return make_cert(m_signer, rng, m_ca_sig_algo, req.raw_public_key(), - Certificate_Properties(not_before, - not_after, - m_cert.subject_dn(), - req.subject_dn()), + not_before, not_after, + m_cert.subject_dn(), req.subject_dn(), extensions); } @@ -93,7 +90,10 @@ X509_Certificate X509_CA::make_cert(PK_Signer* signer, RandomNumberGenerator& rng, const AlgorithmIdentifier& sig_algo, const std::vector& pub_key, - const Certificate_Properties properties, + const X509_Time& not_before, + const X509_Time& not_after, + const X509_DN& issuer_dn, + const X509_DN& subject_dn, const Extensions& extensions) { const size_t X509_CERT_VERSION = 3; @@ -112,14 +112,14 @@ X509_Certificate X509_CA::make_cert(PK_Signer* signer, .encode(serial_no) .encode(sig_algo) - .encode(properties.get_issuer_dn()) + .encode(issuer_dn) .start_cons(SEQUENCE) - .encode(properties.get_not_before()) - .encode(properties.get_not_after()) + .encode(not_before) + .encode(not_after) .end_cons() - .encode(properties.get_subject_dn()) + .encode(subject_dn) .raw_bytes(pub_key) .start_explicit(3) diff --git a/src/lib/cert/x509/x509_ca.h b/src/lib/cert/x509/x509_ca.h index 17e534cfd..ba3724f5e 100644 --- a/src/lib/cert/x509/x509_ca.h +++ b/src/lib/cert/x509/x509_ca.h @@ -1,7 +1,6 @@ /* * X.509 Certificate Authority * (C) 1999-2008 Jack Lloyd -* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -23,26 +22,6 @@ namespace Botan { class BOTAN_DLL X509_CA { public: - class Certificate_Properties - { - public: - Certificate_Properties(X509_Time not_before, X509_Time not_after, - X509_DN issuer_dn, X509_DN subject_dn) - : m_not_before(not_before), m_not_after(not_after), - m_issuer_dn(issuer_dn), m_subject_dn(subject_dn) {} - - const X509_Time& get_not_before() const { return m_not_before; } - const X509_Time& get_not_after() const { return m_not_after; } - const X509_DN& get_issuer_dn() const { return m_issuer_dn; } - const X509_DN& get_subject_dn() const { return m_subject_dn; } - - private: - X509_Time m_not_before; - X509_Time m_not_after; - X509_DN m_issuer_dn; - X509_DN m_subject_dn; - }; - /** * Sign a PKCS#10 Request. * @param req the request to sign @@ -102,7 +81,10 @@ class BOTAN_DLL X509_CA RandomNumberGenerator& rng, const AlgorithmIdentifier& sig_algo, const std::vector& pub_key, - const Certificate_Properties properties, + const X509_Time& not_before, + const X509_Time& not_after, + const X509_DN& issuer_dn, + const X509_DN& subject_dn, const Extensions& extensions); /** diff --git a/src/lib/cert/x509/x509self.cpp b/src/lib/cert/x509/x509self.cpp index 636b9fbb6..8b9aeda09 100644 --- a/src/lib/cert/x509/x509self.cpp +++ b/src/lib/cert/x509/x509self.cpp @@ -1,7 +1,6 @@ /* * PKCS #10/Self Signed Cert Creation * (C) 1999-2008 Jack Lloyd -* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -76,14 +75,9 @@ X509_Certificate create_self_signed_cert(const X509_Cert_Options& opts, extensions.add( new Cert_Extension::Extended_Key_Usage(opts.ex_constraints)); - return X509_CA::make_cert(signer.get(), - rng, - sig_algo, - pub_key, - X509_CA::Certificate_Properties(opts.start, - opts.end, - subject_dn, - subject_dn), + return X509_CA::make_cert(signer.get(), rng, sig_algo, pub_key, + opts.start, opts.end, + subject_dn, subject_dn, extensions); } diff --git a/src/lib/tls/tls_ciphersuite.h b/src/lib/tls/tls_ciphersuite.h index cf9e1587b..1f646cc7e 100644 --- a/src/lib/tls/tls_ciphersuite.h +++ b/src/lib/tls/tls_ciphersuite.h @@ -1,7 +1,6 @@ /* * TLS Cipher Suites * (C) 2004-2011,2012 Jack Lloyd -* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -117,17 +116,32 @@ class BOTAN_DLL Ciphersuite private: - Ciphersuite(u16bit ciphersuite_code, - const char* iana_id, - const char* sig_algo, - const char* kex_algo, - const char* cipher_algo, - size_t cipher_keylen, - size_t nonce_bytes_from_handshake, - size_t nonce_bytes_from_record, - const char* mac_algo, - size_t mac_keylen, - const char* prf_algo = ""); + + Ciphersuite(u16bit ciphersuite_code, + const char* iana_id, + const char* sig_algo, + const char* kex_algo, + const char* cipher_algo, + size_t cipher_keylen, + size_t nonce_bytes_from_handshake, + size_t nonce_bytes_from_record, + const char* mac_algo, + size_t mac_keylen, + const char* prf_algo) : + m_ciphersuite_code(ciphersuite_code), + m_iana_id(iana_id), + m_sig_algo(sig_algo), + m_kex_algo(kex_algo), + m_prf_algo(prf_algo), + m_cipher_algo(cipher_algo), + m_mac_algo(mac_algo), + m_cipher_keylen(cipher_keylen), + m_nonce_bytes_from_handshake(nonce_bytes_from_handshake), + m_nonce_bytes_from_record(nonce_bytes_from_record), + m_mac_keylen(mac_keylen) + { + } + u16bit m_ciphersuite_code = 0; /* diff --git a/src/lib/tls/tls_client.h b/src/lib/tls/tls_client.h index 6bdff8c53..8a45c5444 100644 --- a/src/lib/tls/tls_client.h +++ b/src/lib/tls/tls_client.h @@ -80,7 +80,7 @@ class BOTAN_DLL Client final : public Channel } const std::vector& get_next_protocols() - { + { return m_next_protocols; } diff --git a/src/lib/tls/tls_record.cpp b/src/lib/tls/tls_record.cpp index 5fda1fbb4..e028c43a0 100644 --- a/src/lib/tls/tls_record.cpp +++ b/src/lib/tls/tls_record.cpp @@ -567,7 +567,7 @@ size_t read_dtls_record(secure_vector& readbuf, BOTAN_ASSERT(rec.get_protocol_version()->is_datagram_protocol(), "Expected DTLS"); const size_t record_size = make_u16bit(readbuf[DTLS_HEADER_SIZE-2], - readbuf[DTLS_HEADER_SIZE-1]); + readbuf[DTLS_HEADER_SIZE-1]); if(record_size > MAX_CIPHERTEXT_SIZE) throw TLS_Exception(Alert::RECORD_OVERFLOW, -- cgit v1.2.3 From 3f4e00b7d856d9176d0332c5eb65b4afa406544f Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Wed, 10 Aug 2016 02:42:35 -0400 Subject: Merge GH #570 X509_Certificate APIs --- src/lib/cert/x509/ocsp.cpp | 2 +- src/lib/cert/x509/x509cert.cpp | 35 +++++++++++++++++++++++++++++++---- src/lib/cert/x509/x509cert.h | 30 ++++++++++++++++++++++++++---- 3 files changed, 58 insertions(+), 9 deletions(-) (limited to 'src/lib/cert') diff --git a/src/lib/cert/x509/ocsp.cpp b/src/lib/cert/x509/ocsp.cpp index 4f4a3aece..df8df3b39 100644 --- a/src/lib/cert/x509/ocsp.cpp +++ b/src/lib/cert/x509/ocsp.cpp @@ -81,7 +81,7 @@ void check_signature(const std::vector& tbs_response, // Otherwise attempt to chain the signing cert to a trust root - if(!certs[0].allowed_usage("PKIX.OCSPSigning")) + if(!certs[0].allowed_extended_usage("PKIX.OCSPSigning")) throw Exception("OCSP response cert does not allow OCSP signing"); auto result = x509_path_validate(certs, Path_Validation_Restrictions(), trusted_roots); diff --git a/src/lib/cert/x509/x509cert.cpp b/src/lib/cert/x509/x509cert.cpp index d7da00af0..bd3aff6d5 100644 --- a/src/lib/cert/x509/x509cert.cpp +++ b/src/lib/cert/x509/x509cert.cpp @@ -260,7 +260,7 @@ bool X509_Certificate::allowed_usage(Key_Constraints usage) const return ((constraints() & usage) != 0); } -bool X509_Certificate::allowed_usage(const std::string& usage) const +bool X509_Certificate::allowed_extended_usage(const std::string& usage) const { const std::vector ex = ex_constraints(); @@ -281,13 +281,13 @@ bool X509_Certificate::allowed_usage(Usage_Type usage) const return true; case Usage_Type::TLS_SERVER_AUTH: - return allowed_usage(Key_Constraints(DATA_ENCIPHERMENT | KEY_ENCIPHERMENT | DIGITAL_SIGNATURE)) && allowed_usage("PKIX.ServerAuth"); + return allowed_usage(Key_Constraints(DATA_ENCIPHERMENT | KEY_ENCIPHERMENT | DIGITAL_SIGNATURE)) && allowed_extended_usage("PKIX.ServerAuth"); case Usage_Type::TLS_CLIENT_AUTH: - return allowed_usage(Key_Constraints(DIGITAL_SIGNATURE | NON_REPUDIATION)) && allowed_usage("PKIX.ClientAuth"); + return allowed_usage(Key_Constraints(DIGITAL_SIGNATURE | NON_REPUDIATION)) && allowed_extended_usage("PKIX.ClientAuth"); case Usage_Type::OCSP_RESPONDER: - return allowed_usage(Key_Constraints(DIGITAL_SIGNATURE | NON_REPUDIATION)) && allowed_usage("PKIX.OCSPSigning"); + return allowed_usage(Key_Constraints(DIGITAL_SIGNATURE | NON_REPUDIATION)) && allowed_extended_usage("PKIX.OCSPSigning"); case Usage_Type::CERTIFICATE_AUTHORITY: return is_CA_cert(); @@ -296,6 +296,33 @@ bool X509_Certificate::allowed_usage(Usage_Type usage) const return false; } +bool X509_Certificate::has_constraints(Key_Constraints constraints) const + { + if(this->constraints() == NO_CONSTRAINTS) + { + return false; + } + + return ((this->constraints() & constraints) != 0); + } + +bool X509_Certificate::has_ex_constraint(const std::string& ex_constraint) const + { + const std::vector ex = ex_constraints(); + + if(ex.empty()) + { + return false; + } + + if(std::find(ex.begin(), ex.end(), ex_constraint) != ex.end()) + { + return true; + } + + return false; + } + /* * Return the path length constraint */ diff --git a/src/lib/cert/x509/x509cert.h b/src/lib/cert/x509/x509cert.h index 2875c8159..eb98f9c3d 100644 --- a/src/lib/cert/x509/x509cert.h +++ b/src/lib/cert/x509/x509cert.h @@ -140,17 +140,39 @@ class BOTAN_DLL X509_Certificate : public X509_Object */ bool is_CA_cert() const; + /** + * Returns true if the specified @param usage is set in the key usage extension + * or if no key usage constraints are set at all. + * To check if a certain key constraint is set in the certificate + * use @see X509_Certificate#has_constraints. + */ bool allowed_usage(Key_Constraints usage) const; /** - * Returns true if and only if name (referring to an extended key - * constraint, eg "PKIX.ServerAuth") is included in the extended - * key extension. + * Returns true if the specified @param usage is set in the extended key usage extension + * or if no extended key usage constraints are set at all. + * To check if a certain extended key constraint is set in the certificate + * use @see X509_Certificate#has_ex_constraint. */ - bool allowed_usage(const std::string& usage) const; + bool allowed_extended_usage(const std::string& usage) const; + /** + * Returns true if the required key and extended key constraints are set in the certificate + * for the specified @param usage or if no key constraints are set in both the key usage + * and extended key usage extension. + */ bool allowed_usage(Usage_Type usage) const; + /// Returns true if the specified @param constraints are included in the key usage extension. + bool has_constraints(Key_Constraints constraints) const; + + /** + * Returns true if and only if @param ex_constraint (referring to an extended key + * constraint, eg "PKIX.ServerAuth") is included in the extended + * key extension. + */ + bool has_ex_constraint(const std::string& ex_constraint) const; + /** * Get the path limit as defined in the BasicConstraints extension of * this certificate. -- cgit v1.2.3 From 422e1891987b4aec7019282a623ebf57c79e7866 Mon Sep 17 00:00:00 2001 From: René Korthaus Date: Thu, 4 Aug 2016 10:55:43 +0200 Subject: Fix GH #425 and run x509 tests with different signature algorithms --- src/lib/cert/x509/key_constraint.cpp | 35 +-------- src/lib/cert/x509/key_constraint.h | 12 +--- src/lib/cert/x509/x509_ca.cpp | 8 ++- src/lib/cert/x509/x509_ext.cpp | 10 +-- src/lib/cert/x509/x509cert.cpp | 7 +- src/tests/unit_x509.cpp | 135 ++++++++++++++++++++++++++++------- 6 files changed, 134 insertions(+), 73 deletions(-) (limited to 'src/lib/cert') diff --git a/src/lib/cert/x509/key_constraint.cpp b/src/lib/cert/x509/key_constraint.cpp index 24791b34a..137c42144 100644 --- a/src/lib/cert/x509/key_constraint.cpp +++ b/src/lib/cert/x509/key_constraint.cpp @@ -1,45 +1,16 @@ /* * KeyUsage * (C) 1999-2007 Jack Lloyd +* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ #include #include -#include namespace Botan { -namespace BER { - -/* -* Decode a BER encoded KeyUsage -*/ -void decode(BER_Decoder& source, Key_Constraints& key_usage) - { - BER_Object obj = source.get_next_object(); - - if(obj.type_tag != BIT_STRING || obj.class_tag != UNIVERSAL) - throw BER_Bad_Tag("Bad tag for usage constraint", - obj.type_tag, obj.class_tag); - if(obj.value.size() != 2 && obj.value.size() != 3) - throw BER_Decoding_Error("Bad size for BITSTRING in usage constraint"); - if(obj.value[0] >= 8) - throw BER_Decoding_Error("Invalid unused bits in usage constraint"); - - const byte mask = (0xFF << obj.value[0]); - obj.value[obj.value.size()-1] &= mask; - - u16bit usage = 0; - for(size_t j = 1; j != obj.value.size(); ++j) - usage = (obj.value[j] << 8) | usage; - - key_usage = Key_Constraints(usage); - } - -} - /* * Find the allowable key constraints */ @@ -54,10 +25,10 @@ Key_Constraints find_constraints(const Public_Key& pub_key, constraints |= KEY_AGREEMENT; if(name == "RSA" || name == "ElGamal") - constraints |= KEY_ENCIPHERMENT | DATA_ENCIPHERMENT; + constraints |= KEY_ENCIPHERMENT | DATA_ENCIPHERMENT | ENCIPHER_ONLY | DECIPHER_ONLY; if(name == "RSA" || name == "RW" || name == "NR" || - name == "DSA" || name == "ECDSA") + name == "DSA" || name == "ECDSA" || name == "ECGDSA" || name == "ECKCDSA") constraints |= DIGITAL_SIGNATURE | NON_REPUDIATION; if(limits) diff --git a/src/lib/cert/x509/key_constraint.h b/src/lib/cert/x509/key_constraint.h index 179e413b5..7ae4e26bd 100644 --- a/src/lib/cert/x509/key_constraint.h +++ b/src/lib/cert/x509/key_constraint.h @@ -1,6 +1,7 @@ /* * Enumerations * (C) 1999-2007 Jack Lloyd +* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -8,7 +9,7 @@ #ifndef BOTAN_ENUMS_H__ #define BOTAN_ENUMS_H__ -#include +#include namespace Botan { @@ -44,15 +45,6 @@ class Public_Key; BOTAN_DLL Key_Constraints find_constraints(const Public_Key& pub_key, Key_Constraints limits); -/** -* BER Decoding Function for key constraints -*/ -namespace BER { - -void BOTAN_DLL decode(BER_Decoder&, Key_Constraints&); - -} - } #endif diff --git a/src/lib/cert/x509/x509_ca.cpp b/src/lib/cert/x509/x509_ca.cpp index 147fdd6ad..d64ade6cd 100644 --- a/src/lib/cert/x509/x509_ca.cpp +++ b/src/lib/cert/x509/x509_ca.cpp @@ -233,11 +233,17 @@ PK_Signer* choose_sig_format(const Private_Key& key, std::string padding; if(algo_name == "RSA") + { padding = "EMSA3"; - else if(algo_name == "DSA" || algo_name == "ECDSA" ) + } + else if(algo_name == "DSA" || algo_name == "ECDSA" || algo_name == "ECGDSA" || algo_name == "ECKCDSA") + { padding = "EMSA1"; + } else + { throw Invalid_Argument("Unknown X.509 signing key type: " + algo_name); + } const Signature_Format format = (key.message_parts() > 1) ? DER_SEQUENCE : IEEE_1363; diff --git a/src/lib/cert/x509/x509_ext.cpp b/src/lib/cert/x509/x509_ext.cpp index 986254bc9..650c20d53 100644 --- a/src/lib/cert/x509/x509_ext.cpp +++ b/src/lib/cert/x509/x509_ext.cpp @@ -1,6 +1,7 @@ /* * X.509 Certificate Extensions * (C) 1999-2010,2012 Jack Lloyd +* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -293,7 +294,9 @@ void Key_Usage::decode_inner(const std::vector& in) u16bit usage = 0; for(size_t i = 1; i != obj.value.size(); ++i) - usage = (obj.value[i] << 8) | usage; + { + usage = (obj.value[i] << 8*(sizeof(usage)-i)) | usage; + } m_constraints = Key_Constraints(usage); } @@ -818,13 +821,12 @@ std::vector Unknown_Critical_Extension::encode_inner() const throw Not_Implemented("Unknown_Critical_Extension encoding"); } -void Unknown_Critical_Extension::decode_inner(const std::vector& buf) +void Unknown_Critical_Extension::decode_inner(const std::vector&) { } -void Unknown_Critical_Extension::contents_to(Data_Store& info, Data_Store&) const +void Unknown_Critical_Extension::contents_to(Data_Store&, Data_Store&) const { - // TODO: textual representation? } } diff --git a/src/lib/cert/x509/x509cert.cpp b/src/lib/cert/x509/x509cert.cpp index bd3aff6d5..110014f0e 100644 --- a/src/lib/cert/x509/x509cert.cpp +++ b/src/lib/cert/x509/x509cert.cpp @@ -1,6 +1,7 @@ /* * X.509 Certificates * (C) 1999-2010,2015 Jack Lloyd +* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -565,7 +566,7 @@ std::string X509_Certificate::to_string() const if(constraints & DIGITAL_SIGNATURE) out << " Digital Signature\n"; if(constraints & NON_REPUDIATION) - out << " Non-Repuidation\n"; + out << " Non-Repudiation\n"; if(constraints & KEY_ENCIPHERMENT) out << " Key Encipherment\n"; if(constraints & DATA_ENCIPHERMENT) @@ -576,6 +577,10 @@ std::string X509_Certificate::to_string() const out << " Cert Sign\n"; if(constraints & CRL_SIGN) out << " CRL Sign\n"; + if(constraints & ENCIPHER_ONLY) + out << " Encipher Only\n"; + if(constraints & DECIPHER_ONLY) + out << " Decipher Only\n"; } std::vector policies = this->policies(); diff --git a/src/tests/unit_x509.cpp b/src/tests/unit_x509.cpp index 93e26eee2..a024377d8 100644 --- a/src/tests/unit_x509.cpp +++ b/src/tests/unit_x509.cpp @@ -1,5 +1,6 @@ /* * (C) 2009 Jack Lloyd +* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -28,6 +29,14 @@ #include #endif +#if defined(BOTAN_HAS_ECGDSA) + #include +#endif + +#if defined(BOTAN_HAS_ECKCDSA) + #include +#endif + #endif namespace Botan_Tests { @@ -56,7 +65,7 @@ Botan::X509_Cert_Options ca_opts() return opts; } -Botan::X509_Cert_Options req_opts1() +Botan::X509_Cert_Options req_opts1(const std::string& algo) { Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing"); @@ -64,6 +73,18 @@ Botan::X509_Cert_Options req_opts1() opts.dns = "botan.randombit.net"; opts.email = "testing@randombit.net"; + opts.not_before("1601012000Z"); + opts.not_after("3001012000Z"); + + if(algo == "RSA") + { + opts.constraints = Botan::Key_Constraints(Botan::DECIPHER_ONLY); + } + else if(algo == "DSA" || algo == "ECDSA" || algo == "ECGDSA" || algo == "ECKCDSA") + { + opts.constraints = Botan::Key_Constraints(Botan::DIGITAL_SIGNATURE); + } + return opts; } @@ -80,19 +101,43 @@ Botan::X509_Cert_Options req_opts2() return opts; } -std::unique_ptr make_a_private_key() +std::unique_ptr make_a_private_key(const std::string& algo) { +#if defined(BOTAN_HAS_RSA) + if(algo == "RSA") + { + return std::unique_ptr(new Botan::RSA_PrivateKey(Test::rng(), 1024)); + } +#endif +#if defined(BOTAN_HAS_DSA) + if(algo == "DSA") + { + Botan::DL_Group grp("dsa/botan/2048"); + return std::unique_ptr(new Botan::DSA_PrivateKey(Test::rng(), grp)); + } +#endif #if defined(BOTAN_HAS_ECDSA) - Botan::EC_Group grp("secp256r1"); - return std::unique_ptr(new Botan::ECDSA_PrivateKey(Test::rng(), grp)); -#elif defined(BOTAN_HAS_RSA) - return std::unique_ptr(new Botan::RSA_PrivateKey(Test::rng(), 1024)); -#elif defined(BOTAN_HAS_DSA) - Botan::DL_Group grp("dsa/botan/2048"); - return std::unique_ptr(new Botan::DSA_PrivateKey(Test::rng(), grp)); -#else - return std::unique_ptr(nullptr); + if(algo == "ECDSA") + { + Botan::EC_Group grp("secp256r1"); + return std::unique_ptr(new Botan::ECDSA_PrivateKey(Test::rng(), grp)); + } +#endif +#if defined(BOTAN_HAS_ECGDSA) + if(algo == "ECGDSA") + { + Botan::EC_Group grp("brainpool256r1"); + return std::unique_ptr(new Botan::ECGDSA_PrivateKey(Test::rng(), grp)); + } #endif +#if defined(BOTAN_HAS_ECKCDSA) + if(algo == "ECKCDSA") + { + Botan::EC_Group grp("brainpool256r1"); + return std::unique_ptr(new Botan::ECKCDSA_PrivateKey(Test::rng(), grp)); + } +#endif + return std::unique_ptr(nullptr); } class X509_Cert_Unit_Tests : public Test @@ -207,22 +252,20 @@ class X509_Cert_Unit_Tests : public Test } }; -std::vector X509_Cert_Unit_Tests::run() +Test::Result test_x509_cert(const std::string& algo) { - std::vector results; Test::Result result("X509 Unit"); const std::string hash_fn = "SHA-256"; /* Create the CA's key and self-signed cert */ - std::unique_ptr ca_key(make_a_private_key()); + std::unique_ptr ca_key(make_a_private_key(algo)); if(!ca_key) { - // Failure because X.509 enabled but no RSA or ECDSA seems off - result.test_failure("Skipping due to no enabled signature algorithms"); - results.push_back(result); - return results; + // Failure because X.509 enabled but requested algorithm is not present + result.test_note("Skipping due to missing signature algorithm: " + algo); + return result; } Botan::X509_Certificate ca_cert = @@ -231,17 +274,20 @@ std::vector X509_Cert_Unit_Tests::run() hash_fn, Test::rng()); + result.test_eq("ca key usage", (ca_cert.constraints() & Botan::Key_Constraints(Botan::KEY_CERT_SIGN | Botan::CRL_SIGN)) == + Botan::Key_Constraints(Botan::KEY_CERT_SIGN | Botan::CRL_SIGN), true); + /* Create user #1's key and cert request */ - std::unique_ptr user1_key(make_a_private_key()); + std::unique_ptr user1_key(make_a_private_key(algo)); Botan::PKCS10_Request user1_req = - Botan::X509::create_cert_req(req_opts1(), + Botan::X509::create_cert_req(req_opts1(algo), *user1_key, hash_fn, Test::rng()); /* Create user #2's key and cert request */ - std::unique_ptr user2_key(make_a_private_key()); + std::unique_ptr user2_key(make_a_private_key(algo)); Botan::PKCS10_Request user2_req = Botan::X509::create_cert_req(req_opts2(), @@ -258,9 +304,35 @@ std::vector X509_Cert_Unit_Tests::run() from_date(2008, 01, 01), from_date(2033, 01, 01)); - Botan::X509_Certificate user2_cert = ca.sign_request(user2_req, Test::rng(), - from_date(2008, 01, 01), - from_date(2033, 01, 01)); + Botan::X509_Certificate user2_cert = + ca.sign_request(user2_req, Test::rng(), + from_date(2008, 01, 01), + from_date(2033, 01, 01)); + + result.test_eq("user1 key usage", (user1_cert.constraints() & req_opts1(algo).constraints) == req_opts1(algo).constraints, true); + + /* Copy, assign and compare */ + Botan::X509_Certificate user1_cert_copy(user1_cert); + result.test_eq("certificate copy", user1_cert == user1_cert_copy, true); + + user1_cert_copy = user1_cert; + result.test_eq("certificate assignment", user1_cert == user1_cert_copy, true); + + Botan::X509_Certificate user1_cert_differ = + ca.sign_request(user1_req, Test::rng(), + from_date(2008, 01, 01), + from_date(2032, 01, 01)); + + result.test_eq("certificate differs", user1_cert == user1_cert_differ, false); + + /* Get cert data */ + result.test_eq("x509 version", user1_cert.x509_version(), size_t(3)); + + result.test_eq("issuer info CN", user1_cert.issuer_info("CN").at(0), ca_opts().common_name); + result.test_eq("issuer info Country", user1_cert.issuer_info("C").at(0), ca_opts().country); + result.test_eq("issuer info Orga", user1_cert.issuer_info("O").at(0), ca_opts().organization); + result.test_eq("issuer info OrgaUnit", user1_cert.issuer_info("OU").at(0), ca_opts().org_unit); + Botan::X509_CRL crl1 = ca.new_crl(Test::rng()); /* Verify the certs */ @@ -316,7 +388,20 @@ std::vector X509_Cert_Unit_Tests::run() result_u2 = Botan::x509_path_validate(user2_cert, restrictions, store); result.test_eq("user 2 still revoked", result_u2.result_string(), revoked_str); - results.push_back(result); + return result; + } + +std::vector X509_Cert_Unit_Tests::run() + { + std::vector results; + const std::vector algos { "RSA", "DSA", "ECDSA", "ECGDSA", "ECKCDSA" }; + Test::Result cert_result("X509 Unit"); + for(const auto& algo : algos) + { + cert_result.merge(test_x509_cert(algo)); + } + + results.push_back(cert_result); results.push_back(test_x509_dates()); return results; } -- cgit v1.2.3 From deef8ba63860efb14c45c5ee1cba2a3faaf8a719 Mon Sep 17 00:00:00 2001 From: René Korthaus Date: Tue, 9 Aug 2016 15:44:29 +0200 Subject: Fix allowed_usage() and add tests for key usage --- src/lib/cert/x509/key_constraint.cpp | 2 +- src/lib/cert/x509/x509cert.cpp | 2 +- src/tests/unit_x509.cpp | 346 ++++++++++++++++++++++------------- 3 files changed, 222 insertions(+), 128 deletions(-) (limited to 'src/lib/cert') diff --git a/src/lib/cert/x509/key_constraint.cpp b/src/lib/cert/x509/key_constraint.cpp index 137c42144..f10105f91 100644 --- a/src/lib/cert/x509/key_constraint.cpp +++ b/src/lib/cert/x509/key_constraint.cpp @@ -29,7 +29,7 @@ Key_Constraints find_constraints(const Public_Key& pub_key, if(name == "RSA" || name == "RW" || name == "NR" || name == "DSA" || name == "ECDSA" || name == "ECGDSA" || name == "ECKCDSA") - constraints |= DIGITAL_SIGNATURE | NON_REPUDIATION; + constraints |= DIGITAL_SIGNATURE | NON_REPUDIATION | KEY_CERT_SIGN | CRL_SIGN; if(limits) constraints &= limits; diff --git a/src/lib/cert/x509/x509cert.cpp b/src/lib/cert/x509/x509cert.cpp index 110014f0e..20a4bca25 100644 --- a/src/lib/cert/x509/x509cert.cpp +++ b/src/lib/cert/x509/x509cert.cpp @@ -258,7 +258,7 @@ bool X509_Certificate::allowed_usage(Key_Constraints usage) const { if(constraints() == NO_CONSTRAINTS) return true; - return ((constraints() & usage) != 0); + return ((constraints() & usage) == usage); } bool X509_Certificate::allowed_extended_usage(const std::string& usage) const diff --git a/src/tests/unit_x509.cpp b/src/tests/unit_x509.cpp index a024377d8..8bbad5028 100644 --- a/src/tests/unit_x509.cpp +++ b/src/tests/unit_x509.cpp @@ -140,134 +140,127 @@ std::unique_ptr make_a_private_key(const std::string& algo) return std::unique_ptr(nullptr); } -class X509_Cert_Unit_Tests : public Test + +Test::Result test_x509_dates() { - public: - std::vector run() override; + Test::Result result("X509_Time"); + + Botan::X509_Time time; + result.confirm("unset time not set", !time.time_is_set()); + time = Botan::X509_Time("0802011822Z", Botan::ASN1_Tag::UTC_TIME); + result.confirm("time set after construction", time.time_is_set()); + result.test_eq("time readable_string", time.readable_string(), "2008/02/01 18:22:00 UTC"); + + const std::vector valid = { + "0802010000Z", + "0802011724Z", + "0406142334Z", + "9906142334Z", + "0006142334Z", + + "080201000000Z", + "080201172412Z", + "040614233433Z", + "990614233444Z", + "000614233455Z", + }; - private: - Test::Result test_x509_dates() - { - Test::Result result("X509_Time"); - - Botan::X509_Time time; - result.confirm("unset time not set", !time.time_is_set()); - time = Botan::X509_Time("0802011822Z", Botan::ASN1_Tag::UTC_TIME); - result.confirm("time set after construction", time.time_is_set()); - result.test_eq("time readable_string", time.readable_string(), "2008/02/01 18:22:00 UTC"); - - const std::vector valid = { - "0802010000Z", - "0802011724Z", - "0406142334Z", - "9906142334Z", - "0006142334Z", - - "080201000000Z", - "080201172412Z", - "040614233433Z", - "990614233444Z", - "000614233455Z", - }; - - // Dates that are valid per X.500 but rejected as unsupported - const std::vector valid_but_unsup = { - "0802010000-0000", - "0802011724+0000", - "0406142334-0500", - "9906142334+0500", - "0006142334-0530", - "0006142334+0530", - - "080201000000-0000", - "080201172412+0000", - "040614233433-0500", - "990614233444+0500", - "000614233455-0530", - "000614233455+0530", - }; - - const std::vector invalid = { - "", - " ", - "2008`02-01", - "9999-02-01", - "2000-02-01 17", - "999921", - - // valid length 13 -> range check - "080201000061Z", // seconds too big (61) - "080201000060Z", // seconds too big (60, leap seconds not covered by the standard) - "0802010000-1Z", // seconds too small (-1) - "080201006000Z", // minutes too big (60) - "080201240000Z", // hours too big (24:00) - - // valid length 13 -> invalid numbers - "08020123112 Z", - "08020123112!Z", - "08020123112,Z", - "08020123112\nZ", - "080201232 33Z", - "080201232!33Z", - "080201232,33Z", - "080201232\n33Z", - "0802012 3344Z", - "0802012!3344Z", - "0802012,3344Z", - "08022\n334455Z", - "08022 334455Z", - "08022!334455Z", - "08022,334455Z", - "08022\n334455Z", - "082 33445511Z", - "082!33445511Z", - "082,33445511Z", - "082\n33445511Z", - "2 2211221122Z", - "2!2211221122Z", - "2,2211221122Z", - "2\n2211221122Z", - - // wrong time zone - "0802010000", - "0802010000z" - }; - - for(auto&& v : valid) - { - Botan::X509_Time t(v, Botan::ASN1_Tag::UTC_TIME); - } + // Dates that are valid per X.500 but rejected as unsupported + const std::vector valid_but_unsup = { + "0802010000-0000", + "0802011724+0000", + "0406142334-0500", + "9906142334+0500", + "0006142334-0530", + "0006142334+0530", + + "080201000000-0000", + "080201172412+0000", + "040614233433-0500", + "990614233444+0500", + "000614233455-0530", + "000614233455+0530", + }; - for(auto&& v : valid_but_unsup) - { - result.test_throws("valid but unsupported", [v]() { Botan::X509_Time t(v, Botan::ASN1_Tag::UTC_TIME); }); - } + const std::vector invalid = { + "", + " ", + "2008`02-01", + "9999-02-01", + "2000-02-01 17", + "999921", + + // valid length 13 -> range check + "080201000061Z", // seconds too big (61) + "080201000060Z", // seconds too big (60, leap seconds not covered by the standard) + "0802010000-1Z", // seconds too small (-1) + "080201006000Z", // minutes too big (60) + "080201240000Z", // hours too big (24:00) + + // valid length 13 -> invalid numbers + "08020123112 Z", + "08020123112!Z", + "08020123112,Z", + "08020123112\nZ", + "080201232 33Z", + "080201232!33Z", + "080201232,33Z", + "080201232\n33Z", + "0802012 3344Z", + "0802012!3344Z", + "0802012,3344Z", + "08022\n334455Z", + "08022 334455Z", + "08022!334455Z", + "08022,334455Z", + "08022\n334455Z", + "082 33445511Z", + "082!33445511Z", + "082,33445511Z", + "082\n33445511Z", + "2 2211221122Z", + "2!2211221122Z", + "2,2211221122Z", + "2\n2211221122Z", + + // wrong time zone + "0802010000", + "0802010000z" + }; - for(auto&& v : invalid) - { - result.test_throws("invalid", [v]() { Botan::X509_Time t(v, Botan::ASN1_Tag::UTC_TIME); }); - } + for(auto&& v : valid) + { + Botan::X509_Time t(v, Botan::ASN1_Tag::UTC_TIME); + } - return result; - } - }; + for(auto&& v : valid_but_unsup) + { + result.test_throws("valid but unsupported", [v]() { Botan::X509_Time t(v, Botan::ASN1_Tag::UTC_TIME); }); + } -Test::Result test_x509_cert(const std::string& algo) + for(auto&& v : invalid) + { + result.test_throws("invalid", [v]() { Botan::X509_Time t(v, Botan::ASN1_Tag::UTC_TIME); }); + } + + return result; + } + +Test::Result test_x509_cert(const std::string& sig_algo, const std::string& hash_fn = "SHA-256") { Test::Result result("X509 Unit"); - const std::string hash_fn = "SHA-256"; - /* Create the CA's key and self-signed cert */ - std::unique_ptr ca_key(make_a_private_key(algo)); + std::unique_ptr ca_key(make_a_private_key(sig_algo)); if(!ca_key) { - // Failure because X.509 enabled but requested algorithm is not present - result.test_note("Skipping due to missing signature algorithm: " + algo); + // Failure because X.509 enabled but requested signature algorithm is not present + result.test_note("Skipping due to missing signature algorithm: " + sig_algo); return result; } + /* Create the self-signed cert */ Botan::X509_Certificate ca_cert = Botan::X509::create_self_signed_cert(ca_opts(), *ca_key, @@ -278,16 +271,16 @@ Test::Result test_x509_cert(const std::string& algo) Botan::Key_Constraints(Botan::KEY_CERT_SIGN | Botan::CRL_SIGN), true); /* Create user #1's key and cert request */ - std::unique_ptr user1_key(make_a_private_key(algo)); + std::unique_ptr user1_key(make_a_private_key(sig_algo)); Botan::PKCS10_Request user1_req = - Botan::X509::create_cert_req(req_opts1(algo), + Botan::X509::create_cert_req(req_opts1(sig_algo), *user1_key, hash_fn, Test::rng()); /* Create user #2's key and cert request */ - std::unique_ptr user2_key(make_a_private_key(algo)); + std::unique_ptr user2_key(make_a_private_key(sig_algo)); Botan::PKCS10_Request user2_req = Botan::X509::create_cert_req(req_opts2(), @@ -309,7 +302,7 @@ Test::Result test_x509_cert(const std::string& algo) from_date(2008, 01, 01), from_date(2033, 01, 01)); - result.test_eq("user1 key usage", (user1_cert.constraints() & req_opts1(algo).constraints) == req_opts1(algo).constraints, true); + result.test_eq("user1 key usage", (user1_cert.constraints() & req_opts1(sig_algo).constraints) == req_opts1(sig_algo).constraints, true); /* Copy, assign and compare */ Botan::X509_Certificate user1_cert_copy(user1_cert); @@ -338,7 +331,7 @@ Test::Result test_x509_cert(const std::string& algo) /* Verify the certs */ Botan::Certificate_Store_In_Memory store; - store.add_certificate(ca_cert); + store.add_certificate(ca.ca_certificate()); Botan::Path_Validation_Restrictions restrictions(false); @@ -391,21 +384,122 @@ Test::Result test_x509_cert(const std::string& algo) return result; } -std::vector X509_Cert_Unit_Tests::run() +Test::Result test_usage(const std::string& sig_algo, const std::string& hash_fn = "SHA-256") { - std::vector results; - const std::vector algos { "RSA", "DSA", "ECDSA", "ECGDSA", "ECKCDSA" }; - Test::Result cert_result("X509 Unit"); - for(const auto& algo : algos) + using Botan::Key_Constraints; + + Test::Result result("X509 Usage"); + + /* Create the CA's key and self-signed cert */ + std::unique_ptr ca_key(make_a_private_key(sig_algo)); + + if(!ca_key) { - cert_result.merge(test_x509_cert(algo)); + // Failure because X.509 enabled but requested signature algorithm is not present + result.test_note("Skipping due to missing signature algorithm: " + sig_algo); + return result; } - results.push_back(cert_result); - results.push_back(test_x509_dates()); - return results; + /* Create the self-signed cert */ + Botan::X509_Certificate ca_cert = + Botan::X509::create_self_signed_cert(ca_opts(), + *ca_key, + hash_fn, + Test::rng()); + + /* Create the CA object */ + Botan::X509_CA ca(ca_cert, *ca_key, hash_fn); + + std::unique_ptr user1_key(make_a_private_key(sig_algo)); + + Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing"); + opts.constraints = Key_Constraints::DIGITAL_SIGNATURE; + + Botan::PKCS10_Request user1_req = + Botan::X509::create_cert_req(opts, + *user1_key, + hash_fn, + Test::rng()); + + Botan::X509_Certificate user1_cert = + ca.sign_request(user1_req, Test::rng(), + from_date(2008, 01, 01), + from_date(2033, 01, 01)); + + // cert only allows digitalSignature, but we check for both digitalSignature and cRLSign + result.test_eq("key usage cRLSign not allowed", user1_cert.allowed_usage(Key_Constraints(Key_Constraints::DIGITAL_SIGNATURE | + Key_Constraints::CRL_SIGN)), false); + + // cert only allows digitalSignature, so checking for only that should be ok + result.confirm("key usage digitalSignature allowed", user1_cert.allowed_usage(Key_Constraints::DIGITAL_SIGNATURE)); + + opts.constraints = Key_Constraints(Key_Constraints::DIGITAL_SIGNATURE | Key_Constraints::CRL_SIGN); + + Botan::PKCS10_Request mult_usage_req = + Botan::X509::create_cert_req(opts, + *user1_key, + hash_fn, + Test::rng()); + + Botan::X509_Certificate mult_usage_cert = + ca.sign_request(mult_usage_req, Test::rng(), + from_date(2008, 01, 01), + from_date(2033, 01, 01)); + + // cert allows multiple usages, so each one of them as well as both together should be allowed + result.confirm("key usage multiple digitalSignature allowed", mult_usage_cert.allowed_usage(Key_Constraints::DIGITAL_SIGNATURE)); + result.confirm("key usage multiple cRLSign allowed", mult_usage_cert.allowed_usage(Key_Constraints::CRL_SIGN)); + result.confirm("key usage multiple digitalSignature and cRLSign allowed", mult_usage_cert.allowed_usage( + Key_Constraints(Key_Constraints::DIGITAL_SIGNATURE | Key_Constraints::CRL_SIGN))); + + opts.constraints = Key_Constraints::NO_CONSTRAINTS; + + Botan::PKCS10_Request no_usage_req = + Botan::X509::create_cert_req(opts, + *user1_key, + hash_fn, + Test::rng()); + + Botan::X509_Certificate no_usage_cert = + ca.sign_request(no_usage_req, Test::rng(), + from_date(2008, 01, 01), + from_date(2033, 01, 01)); + + // cert allows every usage + result.confirm("key usage digitalSignature allowed", no_usage_cert.allowed_usage(Key_Constraints::DIGITAL_SIGNATURE)); + result.confirm("key usage cRLSign allowed", no_usage_cert.allowed_usage(Key_Constraints::CRL_SIGN)); + + return result; } + +class X509_Cert_Unit_Tests : public Test + { + public: + std::vector run() override + { + std::vector results; + const std::vector sig_algos { "RSA", "DSA", "ECDSA", "ECGDSA", "ECKCDSA" }; + Test::Result cert_result("X509 Unit"); + for(const auto& algo : sig_algos) + { + cert_result.merge(test_x509_cert(algo)); + } + + results.push_back(cert_result); + results.push_back(test_x509_dates()); + + Test::Result usage_result("X509 Usage"); + for(const auto& algo : sig_algos) + { + usage_result.merge(test_usage(algo)); + } + results.push_back(usage_result); + + return results; + } + }; + BOTAN_REGISTER_TEST("unit_x509", X509_Cert_Unit_Tests); #endif -- cgit v1.2.3 From 6cbff45093199d821dee7ee74380474300f49948 Mon Sep 17 00:00:00 2001 From: René Korthaus Date: Fri, 19 Aug 2016 14:16:13 +0200 Subject: Rename find_constraints() and let it throw instead of returning a combination --- src/lib/cert/x509/key_constraint.cpp | 30 ++++--- src/lib/cert/x509/key_constraint.h | 16 ++-- src/lib/cert/x509/x509_ca.cpp | 10 ++- src/lib/cert/x509/x509self.cpp | 35 ++++++-- src/tests/unit_x509.cpp | 160 ++++++++++++++++++++++++++++++++++- 5 files changed, 218 insertions(+), 33 deletions(-) (limited to 'src/lib/cert') diff --git a/src/lib/cert/x509/key_constraint.cpp b/src/lib/cert/x509/key_constraint.cpp index f10105f91..a90af013c 100644 --- a/src/lib/cert/x509/key_constraint.cpp +++ b/src/lib/cert/x509/key_constraint.cpp @@ -12,29 +12,35 @@ namespace Botan { /* -* Find the allowable key constraints +* Make sure the given key constraints are permitted for the given key type */ -Key_Constraints find_constraints(const Public_Key& pub_key, - Key_Constraints limits) +void verify_cert_constraints_valid_for_key_type(const Public_Key& pub_key, + Key_Constraints constraints) { const std::string name = pub_key.algo_name(); - size_t constraints = 0; + size_t permitted = 0; if(name == "DH" || name == "ECDH") - constraints |= KEY_AGREEMENT; + { + permitted |= KEY_AGREEMENT | ENCIPHER_ONLY | DECIPHER_ONLY; + } if(name == "RSA" || name == "ElGamal") - constraints |= KEY_ENCIPHERMENT | DATA_ENCIPHERMENT | ENCIPHER_ONLY | DECIPHER_ONLY; + { + permitted |= KEY_ENCIPHERMENT | DATA_ENCIPHERMENT; + } if(name == "RSA" || name == "RW" || name == "NR" || name == "DSA" || name == "ECDSA" || name == "ECGDSA" || name == "ECKCDSA") - constraints |= DIGITAL_SIGNATURE | NON_REPUDIATION | KEY_CERT_SIGN | CRL_SIGN; - - if(limits) - constraints &= limits; - - return Key_Constraints(constraints); + { + permitted |= DIGITAL_SIGNATURE | NON_REPUDIATION | KEY_CERT_SIGN | CRL_SIGN; + } + + if ( ( constraints & permitted ) != constraints ) + { + throw Exception("Constraint not permitted for key type " + name); + } } } diff --git a/src/lib/cert/x509/key_constraint.h b/src/lib/cert/x509/key_constraint.h index 7ae4e26bd..b67eb7010 100644 --- a/src/lib/cert/x509/key_constraint.h +++ b/src/lib/cert/x509/key_constraint.h @@ -33,17 +33,13 @@ enum Key_Constraints { class Public_Key; /** -* Create the key constraints for a specific public key. -* @param pub_key the public key from which the basic set of -* constraints to be placed in the return value is derived -* @param limits additional limits that will be incorporated into the -* return value -* @return combination of key type specific constraints and -* additional limits +* Check that key constraints are permitted for a specific public key. +* @param pub_key the public key on which the constraints shall be enforced on +* @param constrains the constraints that shall be enforced on the key +* @throw Exception if the given constraints are not permitted for this key */ - -BOTAN_DLL Key_Constraints find_constraints(const Public_Key& pub_key, - Key_Constraints limits); +BOTAN_DLL void verify_cert_constraints_valid_for_key_type(const Public_Key& pub_key, + Key_Constraints constraints); } diff --git a/src/lib/cert/x509/x509_ca.cpp b/src/lib/cert/x509/x509_ca.cpp index d64ade6cd..58c6676f4 100644 --- a/src/lib/cert/x509/x509_ca.cpp +++ b/src/lib/cert/x509/x509_ca.cpp @@ -52,11 +52,14 @@ X509_Certificate X509_CA::sign_request(const PKCS10_Request& req, { Key_Constraints constraints; if(req.is_CA()) + { constraints = Key_Constraints(KEY_CERT_SIGN | CRL_SIGN); + } else { std::unique_ptr key(req.subject_public_key()); - constraints = find_constraints(*key, req.constraints()); + verify_cert_constraints_valid_for_key_type(*key, req.constraints()); + constraints = req.constraints(); } Extensions extensions; @@ -65,7 +68,10 @@ X509_Certificate X509_CA::sign_request(const PKCS10_Request& req, new Cert_Extension::Basic_Constraints(req.is_CA(), req.path_limit()), true); - extensions.add(new Cert_Extension::Key_Usage(constraints), true); + if(constraints != NO_CONSTRAINTS) + { + extensions.add(new Cert_Extension::Key_Usage(constraints), true); + } extensions.add(new Cert_Extension::Authority_Key_ID(m_cert.subject_key_id())); extensions.add(new Cert_Extension::Subject_Key_ID(req.raw_public_key())); diff --git a/src/lib/cert/x509/x509self.cpp b/src/lib/cert/x509/x509self.cpp index 8b9aeda09..102e24f77 100644 --- a/src/lib/cert/x509/x509self.cpp +++ b/src/lib/cert/x509/x509self.cpp @@ -55,9 +55,14 @@ X509_Certificate create_self_signed_cert(const X509_Cert_Options& opts, Key_Constraints constraints; if(opts.is_CA) + { constraints = Key_Constraints(KEY_CERT_SIGN | CRL_SIGN); + } else - constraints = find_constraints(key, opts.constraints); + { + verify_cert_constraints_valid_for_key_type(key, opts.constraints); + constraints = opts.constraints; + } Extensions extensions; @@ -65,7 +70,10 @@ X509_Certificate create_self_signed_cert(const X509_Cert_Options& opts, new Cert_Extension::Basic_Constraints(opts.is_CA, opts.path_limit), true); - extensions.add(new Cert_Extension::Key_Usage(constraints), true); + if(constraints != NO_CONSTRAINTS) + { + extensions.add(new Cert_Extension::Key_Usage(constraints), true); + } extensions.add(new Cert_Extension::Subject_Key_ID(pub_key)); @@ -99,16 +107,27 @@ PKCS10_Request create_cert_req(const X509_Cert_Options& opts, const size_t PKCS10_VERSION = 0; + Key_Constraints constraints; + if(opts.is_CA) + { + constraints = Key_Constraints(KEY_CERT_SIGN | CRL_SIGN); + } + else + { + verify_cert_constraints_valid_for_key_type(key, opts.constraints); + constraints = opts.constraints; + } + Extensions extensions; extensions.add( new Cert_Extension::Basic_Constraints(opts.is_CA, opts.path_limit)); - extensions.add( - new Cert_Extension::Key_Usage( - opts.is_CA ? Key_Constraints(KEY_CERT_SIGN | CRL_SIGN) : - find_constraints(key, opts.constraints) - ) - ); + + if(constraints != NO_CONSTRAINTS) + { + extensions.add( + new Cert_Extension::Key_Usage(constraints)); + } extensions.add( new Cert_Extension::Extended_Key_Usage(opts.ex_constraints)); extensions.add( diff --git a/src/tests/unit_x509.cpp b/src/tests/unit_x509.cpp index 5110c7d23..4d3c63a1b 100644 --- a/src/tests/unit_x509.cpp +++ b/src/tests/unit_x509.cpp @@ -78,7 +78,7 @@ Botan::X509_Cert_Options req_opts1(const std::string& algo) if(algo == "RSA") { - opts.constraints = Botan::Key_Constraints(Botan::DECIPHER_ONLY); + opts.constraints = Botan::Key_Constraints(Botan::KEY_ENCIPHERMENT); } else if(algo == "DSA" || algo == "ECDSA" || algo == "ECGDSA" || algo == "ECKCDSA") { @@ -472,6 +472,153 @@ Test::Result test_usage(const std::string& sig_algo, const std::string& hash_fn return result; } +using Botan::Key_Constraints; + +/** +* @brief Some typical key usage scenarios (taken from RFC 5280, sec. 4.2.1.3) +*/ +struct typical_usage_constraints + { + // ALL constraints are not typical at all, but we use them for a negative test + Key_Constraints all = Key_Constraints( + Key_Constraints::DIGITAL_SIGNATURE | Key_Constraints::NON_REPUDIATION | Key_Constraints::KEY_ENCIPHERMENT | + Key_Constraints::DATA_ENCIPHERMENT | Key_Constraints::KEY_AGREEMENT | Key_Constraints::KEY_CERT_SIGN | + Key_Constraints::CRL_SIGN | Key_Constraints::ENCIPHER_ONLY | Key_Constraints::DECIPHER_ONLY); + + Key_Constraints ca = Key_Constraints(Key_Constraints::KEY_CERT_SIGN); + Key_Constraints sign_data = Key_Constraints(Key_Constraints::DIGITAL_SIGNATURE); + Key_Constraints non_repudiation = Key_Constraints(Key_Constraints::NON_REPUDIATION | Key_Constraints::DIGITAL_SIGNATURE); + Key_Constraints key_encipherment = Key_Constraints(Key_Constraints::KEY_ENCIPHERMENT); + Key_Constraints data_encipherment = Key_Constraints(Key_Constraints::DATA_ENCIPHERMENT); + Key_Constraints key_agreement = Key_Constraints(Key_Constraints::KEY_AGREEMENT); + Key_Constraints key_agreement_encipher_only = Key_Constraints(Key_Constraints::KEY_AGREEMENT | Key_Constraints::ENCIPHER_ONLY); + Key_Constraints key_agreement_decipher_only = Key_Constraints(Key_Constraints::KEY_AGREEMENT | Key_Constraints::DECIPHER_ONLY); + Key_Constraints crl_sign = Key_Constraints(Key_Constraints::CRL_SIGN); + Key_Constraints sign_everything = Key_Constraints(Key_Constraints::DIGITAL_SIGNATURE | Key_Constraints::KEY_CERT_SIGN | Key_Constraints::CRL_SIGN); + }; + + +Test::Result test_valid_constraints(const std::string& pk_algo) + { + Test::Result result("X509 Valid Constraints"); + + std::unique_ptr key(make_a_private_key(pk_algo)); + + if(!key) + { + // Failure because X.509 enabled but requested algorithm is not present + result.test_note("Skipping due to missing signature algorithm: " + pk_algo); + return result; + } + + // should not throw on empty constraints + verify_cert_constraints_valid_for_key_type(*key, Key_Constraints(Key_Constraints::NO_CONSTRAINTS)); + + // now check some typical usage scenarios for the given key type + typical_usage_constraints typical_usage; + + if(pk_algo == "DH" || pk_algo == "ECDH") + { + // DH and ECDH only for key agreement + result.test_throws("all constraints not permitted", [&key, &typical_usage]() { verify_cert_constraints_valid_for_key_type(*key, + typical_usage.all); }); + result.test_throws("cert sign not permitted", [&key, &typical_usage]() { verify_cert_constraints_valid_for_key_type(*key, + typical_usage.ca); }); + result.test_throws("signature not permitted", [&key, &typical_usage]() { verify_cert_constraints_valid_for_key_type(*key, + typical_usage.sign_data); }); + result.test_throws("non repudiation not permitted", [&key, &typical_usage]() { verify_cert_constraints_valid_for_key_type(*key, + typical_usage.non_repudiation); }); + result.test_throws("key encipherment not permitted", [&key, &typical_usage]() { verify_cert_constraints_valid_for_key_type(*key, + typical_usage.key_encipherment); }); + result.test_throws("data encipherment not permitted", [&key, &typical_usage]() { verify_cert_constraints_valid_for_key_type(*key, + typical_usage.data_encipherment); }); + + verify_cert_constraints_valid_for_key_type(*key, typical_usage.key_agreement); + verify_cert_constraints_valid_for_key_type(*key, typical_usage.key_agreement_encipher_only); + verify_cert_constraints_valid_for_key_type(*key, typical_usage.key_agreement_decipher_only); + + result.test_throws("crl sign not permitted", [&key, &typical_usage]() { verify_cert_constraints_valid_for_key_type(*key, + typical_usage.crl_sign); }); + result.test_throws("sign, cert sign, crl sign not permitted", [&key, &typical_usage]() { verify_cert_constraints_valid_for_key_type(*key, + typical_usage.sign_everything); }); + } + else if(pk_algo == "RSA") + { + // RSA can do everything except key agreement + result.test_throws("all constraints not permitted", [&key, &typical_usage]() { verify_cert_constraints_valid_for_key_type(*key, + typical_usage.all); }); + + verify_cert_constraints_valid_for_key_type(*key, typical_usage.ca); + verify_cert_constraints_valid_for_key_type(*key, typical_usage.sign_data); + verify_cert_constraints_valid_for_key_type(*key, typical_usage.non_repudiation); + verify_cert_constraints_valid_for_key_type(*key, typical_usage.key_encipherment); + verify_cert_constraints_valid_for_key_type(*key, typical_usage.data_encipherment); + + result.test_throws("key agreement not permitted", [&key, &typical_usage]() { verify_cert_constraints_valid_for_key_type(*key, + typical_usage.key_agreement); }); + result.test_throws("key agreement, encipher only not permitted", [&key, &typical_usage]() { verify_cert_constraints_valid_for_key_type(*key, + typical_usage.key_agreement_encipher_only); }); + result.test_throws("key agreement, decipher only not permitted", [&key, &typical_usage]() { verify_cert_constraints_valid_for_key_type(*key, + typical_usage.key_agreement_decipher_only); }); + + verify_cert_constraints_valid_for_key_type(*key, typical_usage.crl_sign); + verify_cert_constraints_valid_for_key_type(*key, typical_usage.sign_everything); + } + else if(pk_algo == "ElGamal") + { + // only ElGamal encryption is currently implemented + result.test_throws("all constraints not permitted", [&key, &typical_usage]() { verify_cert_constraints_valid_for_key_type(*key, + typical_usage.all); }); + result.test_throws("cert sign not permitted", [&key, &typical_usage]() { verify_cert_constraints_valid_for_key_type(*key, + typical_usage.ca); }); + + verify_cert_constraints_valid_for_key_type(*key, typical_usage.non_repudiation); + + result.test_throws("key encipherment not permitted", [&key, &typical_usage]() { verify_cert_constraints_valid_for_key_type(*key, + typical_usage.key_encipherment); }); + result.test_throws("data encipherment not permitted", [&key, &typical_usage]() { verify_cert_constraints_valid_for_key_type(*key, + typical_usage.data_encipherment); }); + + result.test_throws("key agreement not permitted", [&key, &typical_usage]() { verify_cert_constraints_valid_for_key_type(*key, + typical_usage.key_agreement); }); + result.test_throws("key agreement, encipher only not permitted", [&key, &typical_usage]() { verify_cert_constraints_valid_for_key_type(*key, + typical_usage.key_agreement_encipher_only); }); + result.test_throws("key agreement, decipher only not permitted", [&key, &typical_usage]() { verify_cert_constraints_valid_for_key_type(*key, + typical_usage.key_agreement_decipher_only); }); + result.test_throws("crl sign not permitted", [&key, &typical_usage]() { verify_cert_constraints_valid_for_key_type(*key, + typical_usage.crl_sign); }); + result.test_throws("sign, cert sign, crl sign not permitted not permitted", [&key, &typical_usage]() { verify_cert_constraints_valid_for_key_type(*key, + typical_usage.sign_everything); }); + } + else if(pk_algo == "RW" || pk_algo == "NR" || pk_algo == "DSA" || + pk_algo == "ECDSA" || pk_algo == "ECGDSA" || pk_algo == "ECKCDSA") + { + // these are signature algorithms only + result.test_throws("all constraints not permitted", [&key, &typical_usage]() { verify_cert_constraints_valid_for_key_type(*key, + typical_usage.all); }); + + verify_cert_constraints_valid_for_key_type(*key, typical_usage.ca); + verify_cert_constraints_valid_for_key_type(*key, typical_usage.sign_data); + verify_cert_constraints_valid_for_key_type(*key, typical_usage.non_repudiation); + + result.test_throws("key encipherment not permitted", [&key, &typical_usage]() { verify_cert_constraints_valid_for_key_type(*key, + typical_usage.key_encipherment); }); + result.test_throws("data encipherment not permitted", [&key, &typical_usage]() { verify_cert_constraints_valid_for_key_type(*key, + typical_usage.data_encipherment); }); + result.test_throws("key agreement not permitted", [&key, &typical_usage]() { verify_cert_constraints_valid_for_key_type(*key, + typical_usage.key_agreement); }); + result.test_throws("key agreement, encipher only not permitted", [&key, &typical_usage]() { verify_cert_constraints_valid_for_key_type(*key, + typical_usage.key_agreement_encipher_only); }); + result.test_throws("key agreement, decipher only not permitted", [&key, &typical_usage]() { verify_cert_constraints_valid_for_key_type(*key, + typical_usage.key_agreement_decipher_only); }); + + verify_cert_constraints_valid_for_key_type(*key, typical_usage.crl_sign); + verify_cert_constraints_valid_for_key_type(*key, typical_usage.sign_everything); + } + + return result; + } + class X509_Cert_Unit_Tests : public Test { @@ -491,6 +638,17 @@ class X509_Cert_Unit_Tests : public Test results.push_back(cert_result); results.push_back(usage_result); + + const std::vector pk_algos { "DH", "ECDH", "RSA", "ElGamal", "RW", "NR", + "DSA", "ECDSA", "ECGDSA", "ECKCDSA" }; + Test::Result valid_constraints_result("X509 Valid Constraints"); + + for(const auto& algo : pk_algos) + { + valid_constraints_result.merge(test_valid_constraints(algo)); + } + + results.push_back(valid_constraints_result); results.push_back(test_x509_dates()); return results; -- cgit v1.2.3 From a09d2df0885137ea6d7af181e3bcc823412850d8 Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Mon, 29 Aug 2016 11:21:36 -0400 Subject: Fix TLS server cert validation problem GH #611 Fallout from #591 --- src/lib/cert/x509/x509cert.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/lib/cert') diff --git a/src/lib/cert/x509/x509cert.cpp b/src/lib/cert/x509/x509cert.cpp index 20a4bca25..cb9b644bc 100644 --- a/src/lib/cert/x509/x509cert.cpp +++ b/src/lib/cert/x509/x509cert.cpp @@ -282,13 +282,13 @@ bool X509_Certificate::allowed_usage(Usage_Type usage) const return true; case Usage_Type::TLS_SERVER_AUTH: - return allowed_usage(Key_Constraints(DATA_ENCIPHERMENT | KEY_ENCIPHERMENT | DIGITAL_SIGNATURE)) && allowed_extended_usage("PKIX.ServerAuth"); + return (allowed_usage(DATA_ENCIPHERMENT) || allowed_usage(KEY_ENCIPHERMENT) || allowed_usage(DIGITAL_SIGNATURE)) && allowed_extended_usage("PKIX.ServerAuth"); case Usage_Type::TLS_CLIENT_AUTH: - return allowed_usage(Key_Constraints(DIGITAL_SIGNATURE | NON_REPUDIATION)) && allowed_extended_usage("PKIX.ClientAuth"); + return (allowed_usage(DIGITAL_SIGNATURE) || allowed_usage(NON_REPUDIATION)) && allowed_extended_usage("PKIX.ClientAuth"); case Usage_Type::OCSP_RESPONDER: - return allowed_usage(Key_Constraints(DIGITAL_SIGNATURE | NON_REPUDIATION)) && allowed_extended_usage("PKIX.OCSPSigning"); + return (allowed_usage(DIGITAL_SIGNATURE) || allowed_usage(NON_REPUDIATION)) && allowed_extended_usage("PKIX.OCSPSigning"); case Usage_Type::CERTIFICATE_AUTHORITY: return is_CA_cert(); -- cgit v1.2.3 From 786831251f378de9f2d1f07cbb843c1d1ee33672 Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Tue, 30 Aug 2016 08:23:43 -0400 Subject: Change allowed_usage key usage checks to match RFC 5280 GH #611 --- src/lib/cert/x509/x509cert.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/lib/cert') diff --git a/src/lib/cert/x509/x509cert.cpp b/src/lib/cert/x509/x509cert.cpp index cb9b644bc..ffedf43f0 100644 --- a/src/lib/cert/x509/x509cert.cpp +++ b/src/lib/cert/x509/x509cert.cpp @@ -276,16 +276,18 @@ bool X509_Certificate::allowed_extended_usage(const std::string& usage) const bool X509_Certificate::allowed_usage(Usage_Type usage) const { + // These follow suggestions in RFC 5280 4.2.1.12 + switch(usage) { case Usage_Type::UNSPECIFIED: return true; case Usage_Type::TLS_SERVER_AUTH: - return (allowed_usage(DATA_ENCIPHERMENT) || allowed_usage(KEY_ENCIPHERMENT) || allowed_usage(DIGITAL_SIGNATURE)) && allowed_extended_usage("PKIX.ServerAuth"); + return (allowed_usage(KEY_AGREEMENT) || allowed_usage(KEY_ENCIPHERMENT) || allowed_usage(DIGITAL_SIGNATURE)) && allowed_extended_usage("PKIX.ServerAuth"); case Usage_Type::TLS_CLIENT_AUTH: - return (allowed_usage(DIGITAL_SIGNATURE) || allowed_usage(NON_REPUDIATION)) && allowed_extended_usage("PKIX.ClientAuth"); + return (allowed_usage(DIGITAL_SIGNATURE) || allowed_usage(KEY_AGREEMENT)) && allowed_extended_usage("PKIX.ClientAuth"); case Usage_Type::OCSP_RESPONDER: return (allowed_usage(DIGITAL_SIGNATURE) || allowed_usage(NON_REPUDIATION)) && allowed_extended_usage("PKIX.OCSPSigning"); -- cgit v1.2.3