diff options
author | lloyd <[email protected]> | 2010-02-24 13:23:12 +0000 |
---|---|---|
committer | lloyd <[email protected]> | 2010-02-24 13:23:12 +0000 |
commit | 27d84b179913a82849c3994e8724dbec379dab52 (patch) | |
tree | 83634362c2d0ee146ef9a2104722dc3f5491088d /src | |
parent | 7424a5b5102b569e3c052cb195c98b5d1b60ce30 (diff) | |
parent | f849aab38b36c68217e98d1bfc8d8ef8f0e3c027 (diff) |
propagate from branch 'net.randombit.botan' (head 2b67727dd9d1e7fe34f3cb7b7f6715ba42a04918)
to branch 'net.randombit.botan.c++0x' (head 1e2e1596f2b4928c2b7bfba624ea5e4ac69dfdad)
Diffstat (limited to 'src')
64 files changed, 5242 insertions, 1246 deletions
diff --git a/src/cert/cvc/cvc_ado.cpp b/src/cert/cvc/cvc_ado.cpp index 47c972c72..782922354 100644 --- a/src/cert/cvc/cvc_ado.cpp +++ b/src/cert/cvc/cvc_ado.cpp @@ -8,11 +8,10 @@ #include <botan/cvc_ado.h> #include <fstream> -#include <assert.h> namespace Botan { -EAC1_1_ADO::EAC1_1_ADO(std::shared_ptr<DataSource> in) +EAC1_1_ADO::EAC1_1_ADO(DataSource& in) { init(in); do_decode(); @@ -20,7 +19,7 @@ EAC1_1_ADO::EAC1_1_ADO(std::shared_ptr<DataSource> in) EAC1_1_ADO::EAC1_1_ADO(const std::string& in) { - std::shared_ptr<DataSource> stream(new DataSource_Stream(in, true)); + DataSource_Stream stream(in, true); init(stream); do_decode(); } @@ -41,7 +40,7 @@ void EAC1_1_ADO::force_decode() .end_cons() .get_contents(); - std::shared_ptr<DataSource> req_source(new DataSource_Memory(req_bits)); + DataSource_Memory req_source(req_bits); m_req = EAC1_1_Req(req_source); sig_algo = m_req.sig_algo; } @@ -52,15 +51,14 @@ MemoryVector<byte> EAC1_1_ADO::make_signed( RandomNumberGenerator& rng) { SecureVector<byte> concat_sig = - EAC1_1_obj<EAC1_1_ADO>::make_signature(signer, tbs_bits, rng); - assert(concat_sig.size() % 2 == 0); - MemoryVector<byte> result = DER_Encoder() + EAC1_1_obj<EAC1_1_ADO>::make_signature(signer.get(), 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(); - return result; } ASN1_Car EAC1_1_ADO::get_car() const @@ -68,14 +66,15 @@ ASN1_Car EAC1_1_ADO::get_car() const return m_car; } -void EAC1_1_ADO::decode_info(SharedPtrConverter<DataSource> source, +void EAC1_1_ADO::decode_info(DataSource& source, SecureVector<byte> & res_tbs_bits, ECDSA_Signature & res_sig) { SecureVector<byte> concat_sig; SecureVector<byte> cert_inner_bits; ASN1_Car car; - BER_Decoder(*source.get_ptr().get()) + + BER_Decoder(source) .start_cons(ASN1_Tag(7)) .start_cons(ASN1_Tag(33)) .raw_bytes(cert_inner_bits) @@ -89,28 +88,30 @@ void EAC1_1_ADO::decode_info(SharedPtrConverter<DataSource> source, .raw_bytes(cert_inner_bits) .end_cons() .get_contents(); + SecureVector<byte> enc_car = DER_Encoder() .encode(car) .get_contents(); + res_tbs_bits = enc_cert; res_tbs_bits.append(enc_car); res_sig = decode_concatenation(concat_sig); - - } + void EAC1_1_ADO::encode(Pipe& out, X509_Encoding encoding) const { - SecureVector<byte> concat_sig(EAC1_1_obj<EAC1_1_ADO>::m_sig.get_concatenation()); - SecureVector<byte> der = 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(); if(encoding == PEM) throw Invalid_Argument("EAC1_1_ADO::encode() cannot PEM encode an EAC object"); - else - out.write(der); + + SecureVector<byte> concat_sig( + EAC1_1_obj<EAC1_1_ADO>::m_sig.get_concatenation()); + + out.write(DER_Encoder() + .start_cons(ASN1_Tag(7), APPLICATION) + .raw_bytes(tbs_bits) + .encode(concat_sig, OCTET_STRING, ASN1_Tag(55), APPLICATION) + .end_cons() + .get_contents()); } SecureVector<byte> EAC1_1_ADO::tbs_data() const @@ -120,8 +121,6 @@ SecureVector<byte> EAC1_1_ADO::tbs_data() const bool EAC1_1_ADO::operator==(EAC1_1_ADO const& rhs) const { - assert(((this->m_req == rhs.m_req) && (this->tbs_data() == rhs.tbs_data())) || - ((this->m_req != rhs.m_req) && (this->tbs_data() != rhs.tbs_data()))); return (this->get_concat_sig() == rhs.get_concat_sig() && this->tbs_data() == rhs.tbs_data() && this->get_car() == rhs.get_car()); diff --git a/src/cert/cvc/cvc_ado.h b/src/cert/cvc/cvc_ado.h index 5968b1ba4..100888d29 100644 --- a/src/cert/cvc/cvc_ado.h +++ b/src/cert/cvc/cvc_ado.h @@ -38,7 +38,7 @@ class BOTAN_DLL EAC1_1_ADO : public EAC1_1_obj<EAC1_1_ADO> * Construct a CVC ADO request from a data source * @param source the data source */ - EAC1_1_ADO(std::shared_ptr<DataSource> source); + EAC1_1_ADO(DataSource& source); /** * Create a signed CVC ADO request from to be signed (TBS) data @@ -83,7 +83,7 @@ class BOTAN_DLL EAC1_1_ADO : public EAC1_1_obj<EAC1_1_ADO> EAC1_1_Req m_req; void force_decode(); - static void decode_info(SharedPtrConverter<DataSource> source, + static void decode_info(DataSource& source, SecureVector<byte> & res_tbs_bits, ECDSA_Signature & res_sig); }; diff --git a/src/cert/cvc/cvc_ca.cpp b/src/cert/cvc/cvc_ca.cpp index b51c1f4ff..af40fcd05 100644 --- a/src/cert/cvc/cvc_ca.cpp +++ b/src/cert/cvc/cvc_ca.cpp @@ -37,8 +37,7 @@ EAC1_1_CVC EAC1_1_CVC_CA::make_cert(PK_Signer& signer, EAC1_1_CVC::build_cert_body(tbs), rng); - std::shared_ptr<DataSource> source(new DataSource_Memory(signed_cert)); - + DataSource_Memory source(signed_cert); return EAC1_1_CVC(source); } diff --git a/src/cert/cvc/cvc_cert.cpp b/src/cert/cvc/cvc_cert.cpp index 5c2e28c39..4274e143b 100644 --- a/src/cert/cvc/cvc_cert.cpp +++ b/src/cert/cvc/cvc_cert.cpp @@ -56,7 +56,7 @@ void EAC1_1_CVC::force_decode() throw Decoding_Error("CertificateHolderAuthorizationValue was not of length 1"); if(cpi != 0) - throw Decoding_Error("EAC1_1 certificate´s cpi was not 0"); + throw Decoding_Error("EAC1_1 certificate's cpi was not 0"); // FIXME: PK algos have no notion of EAC encoder/decoder currently #if 0 @@ -78,7 +78,7 @@ void EAC1_1_CVC::force_decode() /* * CVC Certificate Constructor */ -EAC1_1_CVC::EAC1_1_CVC(std::shared_ptr<DataSource>& in) +EAC1_1_CVC::EAC1_1_CVC(DataSource& in) { init(in); self_signed = false; @@ -87,7 +87,7 @@ EAC1_1_CVC::EAC1_1_CVC(std::shared_ptr<DataSource>& in) EAC1_1_CVC::EAC1_1_CVC(const std::string& in) { - std::shared_ptr<DataSource> stream(new DataSource_Stream(in, true)); + DataSource_Stream stream(in, true); init(stream); self_signed = false; do_decode(); diff --git a/src/cert/cvc/cvc_cert.h b/src/cert/cvc/cvc_cert.h index 0bc162c0c..ae0c21d7b 100644 --- a/src/cert/cvc/cvc_cert.h +++ b/src/cert/cvc/cvc_cert.h @@ -59,7 +59,7 @@ class BOTAN_DLL EAC1_1_CVC : public EAC1_1_gen_CVC<EAC1_1_CVC>//Signed_Object * Construct a CVC from a data source * @param source the data source */ - EAC1_1_CVC(std::shared_ptr<DataSource>& source); + EAC1_1_CVC(DataSource& source); /** * Construct a CVC from a file diff --git a/src/cert/cvc/cvc_gen_cert.h b/src/cert/cvc/cvc_gen_cert.h index 059a82562..d64812e1e 100644 --- a/src/cert/cvc/cvc_gen_cert.h +++ b/src/cert/cvc/cvc_gen_cert.h @@ -16,7 +16,6 @@ #include <botan/ecdsa.h> #include <botan/ecdsa_sig.h> #include <string> -#include <assert.h> namespace Botan { @@ -87,7 +86,7 @@ class BOTAN_DLL EAC1_1_gen_CVC : public EAC1_1_obj<Derived> // CRTP continuation ASN1_Chr m_chr; bool self_signed; - static void decode_info(SharedPtrConverter<DataSource> source, + static void decode_info(DataSource& source, SecureVector<byte> & res_tbs_bits, ECDSA_Signature & res_sig); @@ -108,8 +107,8 @@ template<typename Derived> MemoryVector<byte> EAC1_1_gen_CVC<Derived>::make_sign const MemoryRegion<byte>& tbs_bits, RandomNumberGenerator& rng) // static { - SecureVector<byte> concat_sig = EAC1_1_obj<Derived>::make_signature(signer, tbs_bits, rng); - assert(concat_sig.size() % 2 == 0); + SecureVector<byte> concat_sig = EAC1_1_obj<Derived>::make_signature(signer.get(), tbs_bits, rng); + return DER_Encoder() .start_cons(ASN1_Tag(33), APPLICATION) .raw_bytes(tbs_bits) @@ -156,12 +155,12 @@ template<typename Derived> void EAC1_1_gen_CVC<Derived>::encode(Pipe& out, X509_ template<typename Derived> void EAC1_1_gen_CVC<Derived>::decode_info( - SharedPtrConverter<DataSource> source, + DataSource& source, SecureVector<byte> & res_tbs_bits, ECDSA_Signature & res_sig) { SecureVector<byte> concat_sig; - BER_Decoder(*source.get_shared().get()) + BER_Decoder(source) .start_cons(ASN1_Tag(33)) .start_cons(ASN1_Tag(78)) .raw_bytes(res_tbs_bits) diff --git a/src/cert/cvc/cvc_req.cpp b/src/cert/cvc/cvc_req.cpp index aa29d8ee6..6df6157ad 100644 --- a/src/cert/cvc/cvc_req.cpp +++ b/src/cert/cvc/cvc_req.cpp @@ -10,19 +10,17 @@ #include <botan/ber_dec.h> #include <botan/pem.h> #include <botan/parsing.h> -#include <assert.h> #include <botan/cvc_key.h> #include <botan/oids.h> #include <botan/look_pk.h> #include <botan/cvc_req.h> -#include <botan/freestore.h> namespace Botan { bool EAC1_1_Req::operator==(EAC1_1_Req const& rhs) const { - return (this->tbs_data() == rhs.tbs_data() - && this->get_concat_sig() == rhs.get_concat_sig()); + return (this->tbs_data() == rhs.tbs_data() && + this->get_concat_sig() == rhs.get_concat_sig()); } void EAC1_1_Req::force_decode() @@ -36,10 +34,9 @@ void EAC1_1_Req::force_decode() .end_cons() .decode(m_chr) .verify_end(); + if(cpi != 0) - { - throw Decoding_Error("EAC1_1 request´s cpi was not 0"); - } + throw Decoding_Error("EAC1_1 requests cpi was not 0"); // FIXME: No EAC support in ECDSA #if 0 @@ -50,7 +47,7 @@ void EAC1_1_Req::force_decode() #endif } -EAC1_1_Req::EAC1_1_Req(std::shared_ptr<DataSource> in) +EAC1_1_Req::EAC1_1_Req(DataSource& in) { init(in); self_signed = true; @@ -59,7 +56,7 @@ EAC1_1_Req::EAC1_1_Req(std::shared_ptr<DataSource> in) EAC1_1_Req::EAC1_1_Req(const std::string& in) { - std::shared_ptr<DataSource> stream(new DataSource_Stream(in, true)); + DataSource_Stream stream(in, true); init(stream); self_signed = true; do_decode(); diff --git a/src/cert/cvc/cvc_req.h b/src/cert/cvc/cvc_req.h index ea05fc157..2abc72c9a 100644 --- a/src/cert/cvc/cvc_req.h +++ b/src/cert/cvc/cvc_req.h @@ -35,7 +35,7 @@ class BOTAN_DLL EAC1_1_Req : public EAC1_1_gen_CVC<EAC1_1_Req> * Construct a CVC request from a data source. * @param source the data source */ - EAC1_1_Req(std::shared_ptr<DataSource> source); + EAC1_1_Req(DataSource& source); /** * Construct a CVC request from a DER encoded CVC reqeust file. diff --git a/src/cert/cvc/cvc_self.cpp b/src/cert/cvc/cvc_self.cpp index dae8f1804..8d782983d 100644 --- a/src/cert/cvc/cvc_self.cpp +++ b/src/cert/cvc/cvc_self.cpp @@ -16,6 +16,7 @@ #include <botan/cvc_ado.h> #include <chrono> #include <sstream> +#include <assert.h> namespace Botan { @@ -42,6 +43,7 @@ std::string padding_and_hash_from_oid(OID const& oid) padding_and_hash.erase(0, padding_and_hash.find("/",0) + 1); return padding_and_hash; } + std::string fixed_len_seqnr(u32bit seqnr, u32bit len) { std::stringstream ss; @@ -132,11 +134,9 @@ EAC1_1_Req create_cvc_req(Private_Key const& key, .encode(chr) .get_contents(); - MemoryVector<byte> signed_cert = - EAC1_1_gen_CVC<EAC1_1_Req>::make_signed(*signer.get(), - EAC1_1_gen_CVC<EAC1_1_Req>::build_cert_body(tbs), rng); + MemoryVector<byte> signed_cert = EAC1_1_gen_CVC<EAC1_1_Req>::make_signed(signer, EAC1_1_gen_CVC<EAC1_1_Req>::build_cert_body(tbs), rng); - std::shared_ptr<DataSource> source(new DataSource_Memory(signed_cert)); + DataSource_Memory source(signed_cert); return EAC1_1_Req(source); } @@ -158,9 +158,9 @@ EAC1_1_ADO create_ado_req(Private_Key const& key, SecureVector<byte> tbs_bits = req.BER_encode(); tbs_bits.append(DER_Encoder().encode(car).get_contents()); + MemoryVector<byte> signed_cert = EAC1_1_ADO::make_signed(signer, tbs_bits, rng); - MemoryVector<byte> signed_cert = EAC1_1_ADO::make_signed(*signer.get(), tbs_bits, rng); - std::shared_ptr<DataSource> source(new DataSource_Memory(signed_cert)); + DataSource_Memory source(signed_cert); return EAC1_1_ADO(source); } @@ -214,7 +214,7 @@ EAC1_1_CVC link_cvca(EAC1_1_CVC const& signer, } if (signer.signature_algorithm() != signee.signature_algorithm()) { - throw Invalid_Argument("link_cvca(): signature algorithms of signer and signee don´t match"); + 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); diff --git a/src/cert/cvc/eac_obj.h b/src/cert/cvc/eac_obj.h index 74d7460dd..419929a19 100644 --- a/src/cert/cvc/eac_obj.h +++ b/src/cert/cvc/eac_obj.h @@ -18,7 +18,6 @@ #include <botan/oids.h> #include <botan/look_pk.h> #include <botan/ecdsa_sig.h> -#include <botan/freestore.h> #include <string> namespace Botan { @@ -50,7 +49,7 @@ class BOTAN_DLL EAC1_1_obj : public EAC_Signed_Object virtual bool check_signature(Public_Key& pub_key) const; protected: - void init(SharedPtrConverter<DataSource> in); + void init(DataSource& in); static SecureVector<byte> make_signature(PK_Signer& signer, const MemoryRegion<byte>& tbs_bits, @@ -78,11 +77,12 @@ EAC1_1_obj<Derived>::make_signature(PK_Signer& signer, return concat_sig; } -template<typename Derived> void EAC1_1_obj<Derived>::init(SharedPtrConverter<DataSource> in) +template<typename Derived> +void EAC1_1_obj<Derived>::init(DataSource& in) { try { - Derived::decode_info(in.get_shared(), tbs_bits, m_sig); + Derived::decode_info(in, tbs_bits, m_sig); } catch(Decoding_Error) { diff --git a/src/cert/cvc/freestore.h b/src/cert/cvc/freestore.h deleted file mode 100644 index 3049dbd13..000000000 --- a/src/cert/cvc/freestore.h +++ /dev/null @@ -1,77 +0,0 @@ -/* -* (C) 2007 Christoph Ludwig -* -* Distributed under the terms of the Botan license -*/ - -#ifndef BOTAN_FREESTORE_H__ -#define BOTAN_FREESTORE_H__ - -#include <botan/build.h> -#include <memory> - -namespace Botan { - -/** -* This class is intended as an function call parameter type and -* enables convenient automatic conversions between plain and smart -* pointer types. It internally stores a SharedPointer which can be -* accessed. -*/ -template<typename T> -class BOTAN_DLL SharedPtrConverter - { - public: - typedef std::shared_ptr<T> SharedPtr; - - /** - * Construct a null pointer equivalent object. - */ - SharedPtrConverter() : ptr() {} - - /** - * Copy constructor. - */ - SharedPtrConverter(SharedPtrConverter const& other) : - ptr(other.ptr) {} - - /** - * Construct a converter object from another pointer type. - * @param p the pointer which shall be set as the internally stored - * pointer value of this converter. - */ - template<typename Ptr> - SharedPtrConverter(Ptr p) - : ptr(p) {} - - /** - * Get the internally stored shared pointer. - * @return the internally stored shared pointer - */ - SharedPtr const& get_ptr() const { return this->ptr; } - - /** - * Get the internally stored shared pointer. - * @return the internally stored shared pointer - */ - SharedPtr get_ptr() { return this->ptr; } - - /** - * Get the internally stored shared pointer. - * @return the internally stored shared pointer - */ - SharedPtr const& get_shared() const { return this->ptr; } - - /** - * Get the internally stored shared pointer. - * @return the internally stored shared pointer - */ - SharedPtr get_shared() { return this->ptr; } - - private: - SharedPtr ptr; - }; - -} - -#endif diff --git a/src/cert/cvc/info.txt b/src/cert/cvc/info.txt index b89441a03..285838379 100644 --- a/src/cert/cvc/info.txt +++ b/src/cert/cvc/info.txt @@ -1,5 +1,4 @@ define CARD_VERIFIABLE_CERTIFICATES - load_on auto <header:public> @@ -13,7 +12,6 @@ cvc_self.h eac_asn_obj.h eac_obj.h ecdsa_sig.h -freestore.h signed_obj.h </header:public> diff --git a/src/cms/cms_dec.cpp b/src/cms/cms_dec.cpp index 222399f6c..038d01869 100644 --- a/src/cms/cms_dec.cpp +++ b/src/cms/cms_dec.cpp @@ -17,7 +17,7 @@ namespace Botan { * CMS_Decoder Constructor */ CMS_Decoder::CMS_Decoder(DataSource& in, const X509_Store& x509store, - User_Interface& ui_ref, PKCS8_PrivateKey* key) : + User_Interface& ui_ref, Private_Key* key) : ui(ui_ref), store(x509store) { status = GOOD; @@ -57,7 +57,7 @@ void CMS_Decoder::initial_read(DataSource&) /* * Add another private key to use */ -void CMS_Decoder::add_key(PKCS8_PrivateKey* key) +void CMS_Decoder::add_key(Private_Key* key) { if(!key) return; diff --git a/src/cms/cms_dec.h b/src/cms/cms_dec.h index 75b61c9cb..a00b44766 100644 --- a/src/cms/cms_dec.h +++ b/src/cms/cms_dec.h @@ -37,10 +37,10 @@ class BOTAN_DLL CMS_Decoder void next_layer() { decode_layer(); } - void add_key(PKCS8_PrivateKey*); + void add_key(Private_Key*); CMS_Decoder(DataSource&, const X509_Store&, User_Interface&, - PKCS8_PrivateKey* = 0); + Private_Key* = 0); private: std::string get_passphrase(const std::string&); void read_econtent(BER_Decoder&); @@ -52,7 +52,7 @@ class BOTAN_DLL CMS_Decoder X509_Store store; std::vector<std::string> passphrases; - std::vector<PKCS8_PrivateKey*> keys; + std::vector<Private_Key*> keys; OID type, next_type; SecureVector<byte> data; diff --git a/src/cms/cms_ealg.cpp b/src/cms/cms_ealg.cpp index 5efa33254..60479a820 100644 --- a/src/cms/cms_ealg.cpp +++ b/src/cms/cms_ealg.cpp @@ -97,7 +97,7 @@ void CMS_Encoder::encrypt(RandomNumberGenerator& rng, { const std::string cipher = choose_algo(user_cipher, "TripleDES"); - std::auto_ptr<X509_PublicKey> key(to.subject_public_key()); + std::auto_ptr<Public_Key> key(to.subject_public_key()); const std::string algo = key->algo_name(); Key_Constraints constraints = to.constraints(); @@ -165,7 +165,7 @@ void CMS_Encoder::encrypt_ktri(RandomNumberGenerator& rng, */ void CMS_Encoder::encrypt_kari(RandomNumberGenerator&, const X509_Certificate&, - X509_PublicKey*, + Public_Key*, const std::string&) { throw Internal_Error("FIXME: unimplemented"); @@ -287,7 +287,7 @@ SecureVector<byte> CMS_Encoder::do_encrypt(RandomNumberGenerator& rng, * Sign a message */ void CMS_Encoder::sign(const X509_Certificate& cert, - const PKCS8_PrivateKey& key, + const Private_Key& key, RandomNumberGenerator& rng, const std::vector<X509_Certificate>& chain, const std::string& hash, diff --git a/src/cms/cms_enc.h b/src/cms/cms_enc.h index 6fdd2b726..b1e18ef7d 100644 --- a/src/cms/cms_enc.h +++ b/src/cms/cms_enc.h @@ -36,7 +36,7 @@ class BOTAN_DLL CMS_Encoder void authenticate(const SymmetricKey&, const std::string& = ""); void sign(const X509_Certificate& cert, - const PKCS8_PrivateKey& key, + const Private_Key& key, RandomNumberGenerator& rng, const std::vector<X509_Certificate>& cert_chain, const std::string& hash, @@ -62,7 +62,7 @@ class BOTAN_DLL CMS_Encoder const X509_Certificate&, PK_Encrypting_Key*, const std::string&); void encrypt_kari(RandomNumberGenerator&, - const X509_Certificate&, X509_PublicKey*, + const X509_Certificate&, Public_Key*, const std::string&); SecureVector<byte> do_encrypt(RandomNumberGenerator& rng, diff --git a/src/libstate/policy.cpp b/src/libstate/policy.cpp index c11fc28ce..3e9c8e122 100644 --- a/src/libstate/policy.cpp +++ b/src/libstate/policy.cpp @@ -38,6 +38,7 @@ void set_default_oids(Library_State& config) add_oid(config, "1.3.6.1.4.1.25258.1.1", "RW"); add_oid(config, "1.3.6.1.4.1.25258.1.2", "NR"); add_oid(config, "1.2.840.10045.2.1", "ECDSA"); // X9.62 + add_oid(config, "1.2.643.2.2.19", "GOST-34.10"); // RFC 4491 /* Ciphers */ add_oid(config, "1.3.14.3.2.7", "DES/CBC"); @@ -103,6 +104,8 @@ void set_default_oids(Library_State& config) add_oid(config, "1.2.840.10045.4.3.3", "ECDSA/EMSA1(SHA-384)"); add_oid(config, "1.2.840.10045.4.3.4", "ECDSA/EMSA1(SHA-512)"); + add_oid(config, "1.2.643.2.2.3", "GOST-34.10/EMSA1(GOST-R-34.11-94)"); + add_oid(config, "1.3.6.1.4.1.25258.2.1.1.1", "RW/EMSA2(RIPEMD-160)"); add_oid(config, "1.3.6.1.4.1.25258.2.1.1.2", "RW/EMSA2(SHA-160)"); add_oid(config, "1.3.6.1.4.1.25258.2.1.1.3", "RW/EMSA2(SHA-224)"); diff --git a/src/math/gfpmath/curve_gfp.cpp b/src/math/gfpmath/curve_gfp.cpp index d88146dd5..e6e69ab0f 100644 --- a/src/math/gfpmath/curve_gfp.cpp +++ b/src/math/gfpmath/curve_gfp.cpp @@ -2,7 +2,7 @@ * Elliptic curves over GF(p) * * (C) 2007 Martin Doering, Christoph Ludwig, Falko Strenzke -* 2008 Jack Lloyd +* 2008-2010 Jack Lloyd * * Distributed under the terms of the Botan license */ @@ -14,152 +14,45 @@ namespace Botan { -void CurveGFp::set_shrd_mod(const std::shared_ptr<GFpModulus> mod) - { - mp_mod = mod; - mA.turn_off_sp_red_mul();// m.m. is not needed, must be trf. back - mB.turn_off_sp_red_mul();// m.m. is not needed, must be trf. back - //ok, above we destroy any evantually computated montg. mult. values, - // but that won't influence performance in usual applications - mA.set_shrd_mod(mod); - mB.set_shrd_mod(mod); - } - CurveGFp::CurveGFp(const GFpElement& a, const GFpElement& b, - const BigInt& p) - : mA(a), - mB(b) + const BigInt& p) : + modulus(p), mA(a), mB(b), + mres_a(mA), mres_b(mB), mres_one(p, 1) { - if(!((p == mA.get_p()) && (p == mB.get_p()))) - { + if(p != mA.get_p() || p != mB.get_p()) throw Invalid_Argument("could not construct curve: moduli of arguments differ"); - } - std::shared_ptr<GFpModulus> p_mod = std::shared_ptr<GFpModulus>(new GFpModulus(p)); - // the above is the creation of the GFpModuls object which will be shared point-wide - // (in the context of a point of course) - set_shrd_mod(p_mod); - } -// copy constructor -CurveGFp::CurveGFp(const CurveGFp& other) - : mA(other.get_a()), - mB(other.get_b()) - { - mp_mod = std::shared_ptr<GFpModulus>(new GFpModulus(*other.mp_mod)); - assert(mp_mod->p_equal_to(mA.get_p())); - assert(mp_mod->p_equal_to(mB.get_p())); - set_shrd_mod(mp_mod); - if(other.mp_mres_a.get()) - { - mp_mres_a = std::shared_ptr<GFpElement>(new GFpElement(*other.mp_mres_a)); - } - if(other.mp_mres_b.get()) - { - mp_mres_b = std::shared_ptr<GFpElement>(new GFpElement(*other.mp_mres_b)); - } - if(other.mp_mres_one.get()) - { - mp_mres_one = std::shared_ptr<GFpElement>(new GFpElement(*other.mp_mres_one)); - } - - } -// assignment operator -const CurveGFp& CurveGFp::operator=(const CurveGFp& other) - { - // for exception safety... - GFpElement a_tmp = other.mA; - GFpElement b_tmp = other.mB; - mA.swap(a_tmp); - mB.swap(b_tmp); - - std::shared_ptr<GFpModulus> p_mod = std::shared_ptr<GFpModulus>(new GFpModulus(*other.mp_mod)); - set_shrd_mod(p_mod); - - // exception safety note: no problem if we have a throw from here on... - if(other.mp_mres_a.get()) - { - mp_mres_a = std::shared_ptr<GFpElement>(new GFpElement(*other.mp_mres_a)); - } - if(other.mp_mres_b.get()) - { - mp_mres_b = std::shared_ptr<GFpElement>(new GFpElement(*other.mp_mres_b)); - } - if(other.mp_mres_one.get()) - { - mp_mres_one = std::shared_ptr<GFpElement>(new GFpElement(*other.mp_mres_one)); - } - return *this; - } - -// getters -const GFpElement& CurveGFp::get_a() const - { - return mA; - } + mres_a.turn_on_sp_red_mul(); + mres_a.get_mres(); -const GFpElement& CurveGFp::get_b() const - { - return mB; - } + mres_b.turn_on_sp_red_mul(); + mres_b.get_mres(); -const BigInt CurveGFp::get_p() const - { - assert(mp_mod.get() != 0); - return mp_mod->get_p(); + mres_one.turn_on_sp_red_mul(); + mres_one.get_mres(); } // swaps the states of *this and other, does not throw void CurveGFp::swap(CurveGFp& other) { - mA.swap(other.mA); - mB.swap(other.mB); - mp_mod.swap(other.mp_mod); - std::swap(mp_mres_a, other.mp_mres_a); - std::swap(mp_mres_b, other.mp_mres_b); - std::swap(mp_mres_one, other.mp_mres_one); - } - -GFpElement const CurveGFp::get_mres_a() const - { - if(mp_mres_a.get() == 0) - { - mp_mres_a = std::shared_ptr<GFpElement>(new GFpElement(mA)); - mp_mres_a->turn_on_sp_red_mul(); - mp_mres_a->get_mres(); - } - return GFpElement(*mp_mres_a); - } - -GFpElement const CurveGFp::get_mres_b() const - { - if(mp_mres_b.get() == 0) - { - mp_mres_b = std::shared_ptr<GFpElement>(new GFpElement(mB)); - mp_mres_b->turn_on_sp_red_mul(); - mp_mres_b->get_mres(); - } - return GFpElement(*mp_mres_b); - } - -std::shared_ptr<GFpElement const> const CurveGFp::get_mres_one() const - { - if(mp_mres_one.get() == 0) - { - mp_mres_one = std::shared_ptr<GFpElement>(new GFpElement(mp_mod->get_p(), 1)); - mp_mres_one->turn_on_sp_red_mul(); - mp_mres_one->get_mres(); - } - return mp_mres_one; + std::swap(mA, other.mA); + std::swap(mB, other.mB); + std::swap(modulus, other.modulus); + std::swap(mres_a, other.mres_a); + std::swap(mres_b, other.mres_b); + std::swap(mres_one, other.mres_one); } bool operator==(const CurveGFp& lhs, const CurveGFp& rhs) { - return (lhs.get_p() == rhs.get_p() && lhs.get_a() == rhs.get_a() && lhs.get_b() == rhs.get_b()); + return (lhs.get_p() == rhs.get_p() && + lhs.get_a() == rhs.get_a() && + lhs.get_b() == rhs.get_b()); } std::ostream& operator<<(std::ostream& output, const CurveGFp& elem) { - return output << "y^2f = x^3 + (" << elem.get_a() << ")x + (" << elem.get_b() << ")"; + return output << "y^2 = x^3 + (" << elem.get_a() << ")x + (" << elem.get_b() << ")"; } } diff --git a/src/math/gfpmath/curve_gfp.h b/src/math/gfpmath/curve_gfp.h index 5b0ec0558..d2ca437fb 100644 --- a/src/math/gfpmath/curve_gfp.h +++ b/src/math/gfpmath/curve_gfp.h @@ -2,6 +2,7 @@ * Elliptic curves over GF(p) * * (C) 2007 Martin Doering, Christoph Ludwig, Falko Strenzke +* 2010 Jack Lloyd * * Distributed under the terms of the Botan license */ @@ -31,28 +32,8 @@ class BOTAN_DLL CurveGFp CurveGFp(const GFpElement& a, const GFpElement& b, const BigInt& p); - /** - * Copy constructor - * @param other The curve to clone - */ - CurveGFp(const CurveGFp& other); - - /** - * Assignment operator - * @param other The curve to use as source for the assignment - */ - const CurveGFp& operator=(const CurveGFp& other); - - /** - * Set the shared GFpModulus object. - * Warning: do not use this function unless you know in detail how - * the sharing of values - * in the various EC related objects works. - * Do NOT spread pointers to a GFpModulus over different threads! - * @param mod a shared pointer to a GFpModulus object suitable for - * *this. - */ - void set_shrd_mod(const std::shared_ptr<GFpModulus> mod); + // CurveGFp(const CurveGFp& other) = default; + // CurveGFp& operator=(const CurveGFp& other) = default; // getters @@ -60,13 +41,13 @@ class BOTAN_DLL CurveGFp * Get coefficient a * @result coefficient a */ - const GFpElement& get_a() const; + const GFpElement& get_a() const { return mA; } /** * Get coefficient b * @result coefficient b */ - const GFpElement& get_b() const; + const GFpElement& get_b() const { return mB; } /** * Get the GFpElement coefficient a transformed @@ -75,7 +56,7 @@ class BOTAN_DLL CurveGFp * function. * @result the coefficient a, transformed to its m-residue */ - GFpElement const get_mres_a() const; + const GFpElement& get_mres_a() const { return mres_a; } /** * Get the GFpElement coefficient b transformed @@ -84,8 +65,7 @@ class BOTAN_DLL CurveGFp * function. * @result the coefficient b, transformed to its m-residue */ - GFpElement const get_mres_b() const; - + const GFpElement& get_mres_b() const { return mres_b; } /** * Get the GFpElement 1 transformed @@ -94,31 +74,13 @@ class BOTAN_DLL CurveGFp * function. * @result the GFpElement 1, transformed to its m-residue */ - std::shared_ptr<GFpElement const> const get_mres_one() const; + const GFpElement& get_mres_one() { return mres_one; } /** * Get prime modulus of the field of the curve * @result prime modulus of the field of the curve */ - BigInt const get_p() const; - /*inline std::shared_ptr<BigInt> const get_ptr_p() const - { - return mp_p; - }*/ - - /** - * Retrieve a shared pointer to the curves GFpModulus object for - * efficient storage and computation of montgomery multiplication - * related data members and functions. Warning: do not use this - * function unless you know in detail how the sharing of values - * in the various EC related objects works. Do NOT spread - * pointers to a GFpModulus over different threads! - * @result a shared pointer to a GFpModulus object - */ - inline std::shared_ptr<GFpModulus> const get_ptr_mod() const - { - return mp_mod; - } + const BigInt& get_p() const { return modulus.get_p(); } /** * swaps the states of *this and other, does not throw @@ -127,12 +89,10 @@ class BOTAN_DLL CurveGFp void swap(CurveGFp& other); private: - std::shared_ptr<GFpModulus> mp_mod; + GFpModulus modulus; GFpElement mA; GFpElement mB; - mutable std::shared_ptr<GFpElement> mp_mres_a; - mutable std::shared_ptr<GFpElement> mp_mres_b; - mutable std::shared_ptr<GFpElement> mp_mres_one; + GFpElement mres_a, mres_b, mres_one; }; // relational operators diff --git a/src/math/gfpmath/gfp_element.cpp b/src/math/gfpmath/gfp_element.cpp index 3f028f34f..3bb4d0002 100644 --- a/src/math/gfpmath/gfp_element.cpp +++ b/src/math/gfpmath/gfp_element.cpp @@ -165,47 +165,20 @@ BigInt montg_trf_to_ordres(const BigInt& m_res, const BigInt& m, const BigInt& r } -GFpElement::GFpElement(const BigInt& p, const BigInt& value, bool use_montgm) - : mp_mod(), - m_value(value %p), - m_use_montgm(use_montgm), - m_is_trf(false) - { - assert(mp_mod.get() == 0); - mp_mod = std::shared_ptr<GFpModulus>(new GFpModulus(p)); - assert(mp_mod->m_p_dash == 0); +GFpElement::GFpElement(const BigInt& p, const BigInt& value, bool use_montgomery) + : modulus(p), m_value(value %p), m_use_montgm(use_montgomery), m_is_trf(false) + { if(m_use_montgm) ensure_montgm_precomp(); } -GFpElement::GFpElement(std::shared_ptr<GFpModulus> const mod, const BigInt& value, bool use_montgm) - : mp_mod(), - m_value(value % mod->m_p), - m_use_montgm(use_montgm), - m_is_trf(false) - { - assert(mp_mod.get() == 0); - mp_mod = mod; - } - -GFpElement::GFpElement(const GFpElement& other) - : m_value(other.m_value), - m_use_montgm(other.m_use_montgm), - m_is_trf(other.m_is_trf) - - { - //creates an independent copy - assert((other.m_is_trf && other.m_use_montgm) || !other.m_is_trf); - mp_mod.reset(new GFpModulus(*other.mp_mod)); // copy-ctor of GFpModulus - } - -void GFpElement::turn_on_sp_red_mul() const +void GFpElement::turn_on_sp_red_mul() { ensure_montgm_precomp(); m_use_montgm = true; } -void GFpElement::turn_off_sp_red_mul() const +void GFpElement::turn_off_sp_red_mul() { if(m_is_trf) { @@ -216,40 +189,25 @@ void GFpElement::turn_off_sp_red_mul() const m_use_montgm = false; } -void GFpElement::ensure_montgm_precomp() const +void GFpElement::ensure_montgm_precomp() { - if((!mp_mod->m_r.is_zero()) && (!mp_mod->m_r_inv.is_zero()) && (!mp_mod->m_p_dash.is_zero())) + if((!modulus.get_r().is_zero()) && (!modulus.get_r_inv().is_zero()) && (!modulus.get_p_dash().is_zero())) { // values are already set, nothing more to do } else { - BigInt tmp_r(montgm_calc_r_oddmod(mp_mod->m_p)); - - BigInt tmp_r_inv(inverse_mod(tmp_r, mp_mod->m_p)); + BigInt tmp_r(montgm_calc_r_oddmod(modulus.get_p())); - BigInt tmp_p_dash(montgm_calc_m_dash(tmp_r, mp_mod->m_p, tmp_r_inv)); + BigInt tmp_r_inv(inverse_mod(tmp_r, modulus.get_p())); - mp_mod->m_r.grow_reg(tmp_r.size()); - mp_mod->m_r_inv.grow_reg(tmp_r_inv.size()); - mp_mod->m_p_dash.grow_reg(tmp_p_dash.size()); + BigInt tmp_p_dash(montgm_calc_m_dash(tmp_r, modulus.get_p(), tmp_r_inv)); - mp_mod->m_r = tmp_r; - mp_mod->m_r_inv = tmp_r_inv; - mp_mod->m_p_dash = tmp_p_dash; - - assert(!mp_mod->m_r.is_zero()); - assert(!mp_mod->m_r_inv.is_zero()); - assert(!mp_mod->m_p_dash.is_zero()); + modulus.reset_values(tmp_p_dash, tmp_r, tmp_r_inv); } } -void GFpElement::set_shrd_mod(std::shared_ptr<GFpModulus> const p_mod) - { - mp_mod = p_mod; - } - void GFpElement::trf_to_mres() const { if(!m_use_montgm) @@ -257,27 +215,27 @@ void GFpElement::trf_to_mres() const throw Illegal_Transformation("GFpElement is not allowed to be transformed to m-residue"); } assert(m_is_trf == false); - assert(!mp_mod->m_r_inv.is_zero()); - assert(!mp_mod->m_p_dash.is_zero()); - m_value = montg_trf_to_mres(m_value, mp_mod->m_r, mp_mod->m_p); + assert(!modulus.get_r_inv().is_zero()); + assert(!modulus.get_p_dash().is_zero()); + m_value = montg_trf_to_mres(m_value, modulus.get_r(), modulus.get_p()); m_is_trf = true; } void GFpElement::trf_to_ordres() const { assert(m_is_trf == true); - m_value = montg_trf_to_ordres(m_value, mp_mod->m_p, mp_mod->m_r_inv); + m_value = montg_trf_to_ordres(m_value, modulus.get_p(), modulus.get_r_inv()); m_is_trf = false; } bool GFpElement::align_operands_res(const GFpElement& lhs, const GFpElement& rhs) //static { - assert(lhs.mp_mod->m_p == rhs.mp_mod->m_p); + assert(lhs.modulus.get_p() == rhs.modulus.get_p()); if(lhs.m_use_montgm && rhs.m_use_montgm) { - assert(rhs.mp_mod->m_p_dash == lhs.mp_mod->m_p_dash); - assert(rhs.mp_mod->m_r == lhs.mp_mod->m_r); - assert(rhs.mp_mod->m_r_inv == lhs.mp_mod->m_r_inv); + assert(rhs.modulus.get_p_dash() == lhs.modulus.get_p_dash()); + assert(rhs.modulus.get_r() == lhs.modulus.get_r()); + assert(rhs.modulus.get_r_inv() == lhs.modulus.get_r_inv()); if(!lhs.m_is_trf && !rhs.m_is_trf) { return false; @@ -327,7 +285,7 @@ bool GFpElement::is_trf_to_mres() const const BigInt& GFpElement::get_p() const { - return (mp_mod->m_p); + return (modulus.get_p()); } const BigInt& GFpElement::get_value() const @@ -345,7 +303,7 @@ const BigInt& GFpElement::get_mres() const if(!m_use_montgm) { // does the following exception really make sense? - // wouldn´t it be better to simply turn on montg.mult. when + // wouldn't it be better to simply turn on montg.mult. when // this explicit request is made? throw Illegal_Transformation("GFpElement is not allowed to be transformed to m-residue"); } @@ -357,107 +315,17 @@ const BigInt& GFpElement::get_mres() const return m_value; } -const GFpElement& GFpElement::operator=(const GFpElement& other) - { - m_value.grow_reg(other.m_value.size()); // grow first for exception safety - - //m_value = other.m_value; - - // m_use_montgm = other.m_use_montgm; - // m_is_trf = other.m_is_trf; - // we want to keep the member pointers, which might be part of a "sharing group" - // but we may not simply overwrite the BigInt values with those of the argument!! - // if ours already contains precomputations, it would be hazardous to - // set them back to zero. - // thus we first check for equality of the moduli, - // then whether either of the two objects already contains - // precomputed values. - - // we also deal with the case were the pointers themsevles are equal: - if(mp_mod.get() == other.mp_mod.get()) - { - // everything ok, we are in the same sharing group anyway, nothing to do - m_value = other.m_value; // cannot throw - m_use_montgm = other.m_use_montgm; - m_is_trf = other.m_is_trf; - return *this; - } - if(mp_mod->m_p != other.mp_mod->m_p) - { - // the moduli are different, this is a special case - // which will not occur in usual applications, - // so we don´t hesitate to simply create new objects - // (we do want to create an independent copy) - mp_mod.reset(new GFpModulus(*other.mp_mod)); // this could throw, - // and because of this - // we haven't modified - // anything so far - m_value = other.m_value; // can't throw - m_use_montgm = other.m_use_montgm; - m_is_trf = other.m_is_trf; - return *this; - } - // exception safety note: from now on we are on the safe - // side with respect to the modulus, - // so we can assign the value now: - m_value = other.m_value; - m_use_montgm = other.m_use_montgm; - m_is_trf = other.m_is_trf; - // the moduli are equal, but we deal with different sharing groups. - // we will NOT fuse the sharing goups - // and we will NOT reset already precomputed values - if(mp_mod->has_precomputations()) - { - // our own sharing group already has precomputed values, - // so nothing to do. - return *this; - } - else - { - // let´s see whether the argument has something for us... - if(other.mp_mod->has_precomputations()) - { - // fetch them for our sharing group - // exc. safety note: grow first - mp_mod->m_p_dash.grow_reg(other.mp_mod->m_p_dash.size()); - mp_mod->m_r.grow_reg(other.mp_mod->m_r.size()); - mp_mod->m_r_inv.grow_reg(other.mp_mod->m_r_inv.size()); - - mp_mod->m_p_dash = other.mp_mod->m_p_dash; - mp_mod->m_r = other.mp_mod->m_r; - mp_mod->m_r_inv = other.mp_mod->m_r_inv; - return *this; - } - } - // our precomputations aren´t set, the arguments neither, - // so we let them alone - return *this; - } - -void GFpElement::share_assign(const GFpElement& other) - { - assert((other.m_is_trf && other.m_use_montgm) || !other.m_is_trf); - - // use grow_to to make it exc safe - m_value.grow_reg(other.m_value.size()); - m_value = other.m_value; - - m_use_montgm = other.m_use_montgm; - m_is_trf = other.m_is_trf; - mp_mod = other.mp_mod; // cannot throw - } - GFpElement& GFpElement::operator+=(const GFpElement& rhs) { GFpElement::align_operands_res(*this, rhs); - workspace = m_value; + BigInt workspace = m_value; workspace += rhs.m_value; - if(workspace >= mp_mod->m_p) - workspace -= mp_mod->m_p; + if(workspace >= modulus.get_p()) + workspace -= modulus.get_p(); m_value = workspace; - assert(m_value < mp_mod->m_p); + assert(m_value < modulus.get_p()); assert(m_value >= 0); return *this; @@ -467,39 +335,39 @@ GFpElement& GFpElement::operator-=(const GFpElement& rhs) { GFpElement::align_operands_res(*this, rhs); - workspace = m_value; + BigInt workspace = m_value; workspace -= rhs.m_value; if(workspace.is_negative()) - workspace += mp_mod->m_p; + workspace += modulus.get_p(); m_value = workspace; - assert(m_value < mp_mod->m_p); + assert(m_value < modulus.get_p()); assert(m_value >= 0); return *this; } GFpElement& GFpElement::operator*= (u32bit rhs) { - workspace = m_value; + BigInt workspace = m_value; workspace *= rhs; - workspace %= mp_mod->m_p; + workspace %= modulus.get_p(); m_value = workspace; return *this; } GFpElement& GFpElement::operator*=(const GFpElement& rhs) { - assert(rhs.mp_mod->m_p == mp_mod->m_p); + assert(rhs.modulus.get_p() == modulus.get_p()); // here, we do not use align_operands_res() for one simple reason: // we want to enforce the transformation to an m-residue, otherwise it would // never happen if(m_use_montgm && rhs.m_use_montgm) { - assert(rhs.mp_mod->m_p == mp_mod->m_p); // is montgm. mult is on, then precomps must be there - assert(rhs.mp_mod->m_p_dash == mp_mod->m_p_dash); - assert(rhs.mp_mod->m_r == mp_mod->m_r); + assert(rhs.modulus.get_p() == modulus.get_p()); // is montgm. mult is on, then precomps must be there + assert(rhs.modulus.get_p_dash() == modulus.get_p_dash()); + assert(rhs.modulus.get_r() == modulus.get_r()); if(!m_is_trf) { trf_to_mres(); @@ -508,8 +376,8 @@ GFpElement& GFpElement::operator*=(const GFpElement& rhs) { rhs.trf_to_mres(); } - workspace = m_value; - montg_mult(m_value, workspace, rhs.m_value, mp_mod->m_p, mp_mod->m_p_dash, mp_mod->m_r); + BigInt workspace = m_value; + montg_mult(m_value, workspace, rhs.m_value, modulus.get_p(), modulus.get_p_dash(), modulus.get_r()); } else // ordinary multiplication { @@ -524,9 +392,9 @@ GFpElement& GFpElement::operator*=(const GFpElement& rhs) rhs.trf_to_ordres(); } - workspace = m_value; + BigInt workspace = m_value; workspace *= rhs.m_value; - workspace %= mp_mod->m_p; + workspace %= modulus.get_p(); m_value = workspace; } return *this; @@ -536,18 +404,17 @@ GFpElement& GFpElement::operator/=(const GFpElement& rhs) { bool use_mres = GFpElement::align_operands_res(*this, rhs); assert((this->m_is_trf && rhs.m_is_trf) || !(this->m_is_trf && rhs.m_is_trf)); - // (internal note: see C86) + if(use_mres) { assert(m_use_montgm && rhs.m_use_montgm); GFpElement rhs_ordres(rhs); rhs_ordres.trf_to_ordres(); rhs_ordres.inverse_in_place(); - workspace = m_value; - workspace *= rhs_ordres.get_value(); - workspace %= mp_mod->m_p; + BigInt workspace = m_value; + workspace *= rhs_ordres.get_value(); + workspace %= modulus.get_p(); m_value = workspace; - } else { @@ -566,30 +433,31 @@ bool GFpElement::is_zero() GFpElement& GFpElement::inverse_in_place() { - m_value = inverse_mod(m_value, mp_mod->m_p); + m_value = inverse_mod(m_value, modulus.get_p()); + if(m_is_trf) { assert(m_use_montgm); - m_value *= mp_mod->m_r; - m_value *= mp_mod->m_r; - m_value %= mp_mod->m_p; + m_value *= modulus.get_r(); + m_value *= modulus.get_r(); + m_value %= modulus.get_p(); } - assert(m_value <= mp_mod->m_p); + assert(m_value <= modulus.get_p()); return *this; } GFpElement& GFpElement::negate() { - m_value = mp_mod->m_p - m_value; - assert(m_value <= mp_mod->m_p); + m_value = modulus.get_p() - m_value; + assert(m_value <= modulus.get_p()); return *this; } void GFpElement::swap(GFpElement& other) { - m_value.swap(other.m_value); - mp_mod.swap(other.mp_mod); + std::swap(m_value, other.m_value); + std::swap(modulus, other.modulus); std::swap<bool>(m_use_montgm,other.m_use_montgm); std::swap<bool>(m_is_trf,other.m_is_trf); } @@ -601,15 +469,9 @@ std::ostream& operator<<(std::ostream& output, const GFpElement& elem) bool operator==(const GFpElement& lhs, const GFpElement& rhs) { - // for effeciency reasons we firstly check whether - //the modulus pointers are different in the first place: - if(lhs.get_ptr_mod() != rhs.get_ptr_mod()) - { - if(lhs.get_p() != rhs.get_p()) - { - return false; - } - } + if(lhs.get_p() != rhs.get_p()) + return false; + // so the modulus is equal, now check the values bool use_mres = GFpElement::align_operands_res(lhs, rhs); diff --git a/src/math/gfpmath/gfp_element.h b/src/math/gfpmath/gfp_element.h index a4d9ac250..538d41a47 100644 --- a/src/math/gfpmath/gfp_element.h +++ b/src/math/gfpmath/gfp_element.h @@ -2,6 +2,7 @@ * Arithmetic for prime fields GF(p) * * (C) 2007 Martin Doering, Christoph Ludwig, Falko Strenzke +* 2009-2010 Jack Lloyd * * Distributed under the terms of the Botan license */ @@ -12,7 +13,6 @@ #include <botan/bigint.h> #include <botan/gfp_modulus.h> #include <iosfwd> -#include <memory> namespace Botan { @@ -38,57 +38,21 @@ class BOTAN_DLL GFpElement * @param value the element value * @param use_montgm whether this object will use Montgomery multiplication */ - explicit GFpElement (const BigInt& p, const BigInt& value, bool use_montgm = false); + GFpElement(const BigInt& p, const BigInt& value, bool use_montgm = true); + // GFpElement(const GFpElement& other) = default; - /** construct an element of GF(p) with the given value (defaults - * to 0). use_montg defaults to false and determines wether - * montgomery multiplications will be use when applying operators - * '*' , '*='. Use this constructor for efficient use of - * Montgomery multiplication in a context with a fixed a modulus. - * Warning: do not use this function unless you know in detail - * about the implications of using the shared GFpModulus objects! - * @param mod shared pointer to the GFpModulus to be shared - * @param value the element value - * @param use_montgm whether this object will use Montgomery multiplication - */ - explicit GFpElement(std::shared_ptr<GFpModulus> const mod, - const BigInt& value, bool use_mongm = false); - - /** - * Copy constructor - * @param other The element to clone - */ - GFpElement(const GFpElement& other); - - /** - * Assignment operator. - * makes *this a totally independent object - * (gives *this independent modulus specific values). - - * @param other The element to assign to our object - */ - const GFpElement& operator=(const GFpElement& other); - - /** - * Works like the assignment operator, but lets - * *this share the modulus dependend value with other. - * Warning: do not use this function unless you know in detail about - * the implications of using - * the shared GFpModulus objects! - * @param other The element to assign to our object - */ - void share_assign(const GFpElement& other); + // const GFpElement& operator=(const GFpElement& other) = default; /** * Switch Montgomery multiplcation optimizations ON */ - void turn_on_sp_red_mul() const; + void turn_on_sp_red_mul(); /** * Switch Montgomery multiplcation optimizations OFF */ - void turn_off_sp_red_mul() const; + void turn_off_sp_red_mul(); /** * += Operator @@ -122,7 +86,7 @@ class BOTAN_DLL GFpElement * @param rhs the value to multiply with the local value * @result *this */ - GFpElement& operator*= (u32bit rhs); + GFpElement& operator*=(u32bit rhs); /** * Negate internal value(*this *= -1 ) @@ -157,31 +121,9 @@ class BOTAN_DLL GFpElement const BigInt& get_value() const; /** - * Returns the shared pointer to the GFpModulus of *this. - * Warning: do not use this function unless you know in detail about - * the implications of using - * the shared GFpModulus objects! - * @result the shared pointer to the GFpModulus of *this - */ - inline std::shared_ptr<GFpModulus> const get_ptr_mod() const - { - return mp_mod; - } - - - /** - * Sets the shared pointer to the GFpModulus of *this. - * Warning: do not use this function unless you know in detail about - * the implications of using - * the shared GFpModulus objects! - * @param mod a shared pointer to a GFpModulus that will be held in *this - */ - void set_shrd_mod(std::shared_ptr<GFpModulus> const mod); - - /** - * Tells whether this GFpElement is currently transformed to it´ m-residue, + * Tells whether this GFpElement is currently transformed to an m-residue, * i.e. in the form x_bar = x * r mod m. - * @result true if it is currently transformed to it´s m-residue. + * @result true if it is currently transformed to its m-residue. */ bool is_trf_to_mres() const; @@ -206,7 +148,7 @@ class BOTAN_DLL GFpElement * in ordinary residue representation (returns false). * m-residue is prefered in case of ambiguity. * does not toggle m_use_montgm of the arguments. - * Don´t be confused about the constness of the arguments: + * Don't be confused about the constness of the arguments: * the transformation between normal residue and m-residue is * considered as leaving the object const. * @param lhs the first operand to be aligned @@ -216,30 +158,22 @@ class BOTAN_DLL GFpElement */ static bool align_operands_res(const GFpElement& lhs, const GFpElement& rhs); - //friend declarations for non-member functions - - friend class Point_Coords_GFp; - /** * swaps the states of *this and other, does not throw! * @param other The value to swap with */ void swap(GFpElement& other); private: - void ensure_montgm_precomp() const; + void ensure_montgm_precomp(); void trf_to_mres() const; void trf_to_ordres() const; - std::shared_ptr<GFpModulus> mp_mod; + GFpModulus modulus; mutable BigInt m_value; // ordinary residue or m-residue respectively - mutable BigInt workspace; // data members for montgomery multiplication - mutable bool m_use_montgm; - //mutable BigInt m_mres; - // this bool tells use whether the m_mres carries - // the actual value (in this case mValue doesn´t) - mutable bool m_is_trf; + bool m_use_montgm; + mutable bool m_is_trf; // if m_value is montgomery }; // relational operators @@ -256,8 +190,8 @@ GFpElement BOTAN_DLL operator-(const GFpElement& lhs); GFpElement BOTAN_DLL operator*(const GFpElement& lhs, const GFpElement& rhs); GFpElement BOTAN_DLL operator/(const GFpElement& lhs, const GFpElement& rhs); -GFpElement BOTAN_DLL operator* (const GFpElement& lhs, u32bit rhs); -GFpElement BOTAN_DLL operator* (u32bit rhs, const GFpElement& lhs); +GFpElement BOTAN_DLL operator*(const GFpElement& lhs, u32bit rhs); +GFpElement BOTAN_DLL operator*(u32bit rhs, const GFpElement& lhs); /** diff --git a/src/math/gfpmath/gfp_modulus.h b/src/math/gfpmath/gfp_modulus.h index 03e8a19e0..fcdd13ee1 100644 --- a/src/math/gfpmath/gfp_modulus.h +++ b/src/math/gfpmath/gfp_modulus.h @@ -22,24 +22,26 @@ class GFpElement; class BOTAN_DLL GFpModulus { public: - friend class GFpElement; /** * Construct a GF(P)-Modulus from a BigInt */ - GFpModulus(BigInt p) + GFpModulus(const BigInt& p) : m_p(p), m_p_dash(), m_r(), m_r_inv() {} + // GFpModulus(const GFpModulus& other) = default; + // GFpModulus& operator=(const GFpModulus& other) = default; + /** * Tells whether the precomputations necessary for the use of the * montgomery multiplication have yet been established. * @result true if the precomputated value are already available. */ - inline bool has_precomputations() const + bool has_precomputations() const { return(!m_p_dash.is_zero() && !m_r.is_zero() && !m_r_inv.is_zero()); } @@ -48,12 +50,12 @@ class BOTAN_DLL GFpModulus * Swaps this with another GFpModulus, does not throw. * @param other the GFpModulus to swap *this with. */ - inline void swap(GFpModulus& other) + void swap(GFpModulus& other) { - m_p.swap(other.m_p); - m_p_dash.swap(other.m_p_dash); - m_r.swap(other.m_r); - m_r_inv.swap(other.m_r_inv); + std::swap(m_p, other.m_p); + std::swap(m_p_dash, other.m_p_dash); + std::swap(m_r, other.m_r); + std::swap(m_r_inv, other.m_r_inv); } /** @@ -61,7 +63,7 @@ class BOTAN_DLL GFpModulus * @param mod the modulus to compare this with * @result true if the modulus of *this and the argument are equal. */ - inline bool p_equal_to(const BigInt& mod) const + bool p_equal_to(const BigInt& mod) const { return (m_p == mod); } @@ -70,7 +72,7 @@ class BOTAN_DLL GFpModulus * Return the modulus of this GFpModulus. * @result the modulus of *this. */ - inline const BigInt& get_p() const + const BigInt& get_p() const { return m_p; } @@ -81,7 +83,7 @@ class BOTAN_DLL GFpModulus * performed! * @result r */ - inline const BigInt& get_r() const + const BigInt& get_r() const { return m_r; } @@ -92,7 +94,7 @@ class BOTAN_DLL GFpModulus * performed! * @result r^{-1} */ - inline const BigInt& get_r_inv() const + const BigInt& get_r_inv() const { return m_r_inv; } @@ -103,17 +105,25 @@ class BOTAN_DLL GFpModulus * performed! * @result p' */ - inline const BigInt& get_p_dash() const + const BigInt& get_p_dash() const { return m_p_dash; } - // default cp-ctor, op= are fine + + void reset_values(const BigInt& new_p_dash, + const BigInt& new_r, + const BigInt& new_r_inv) + { + m_p_dash = new_p_dash; + m_r = new_r; + m_r_inv = new_r_inv; + } private: BigInt m_p; // the modulus itself - mutable BigInt m_p_dash; - mutable BigInt m_r; - mutable BigInt m_r_inv; + BigInt m_p_dash; + BigInt m_r; + BigInt m_r_inv; }; } diff --git a/src/math/gfpmath/point_gfp.cpp b/src/math/gfpmath/point_gfp.cpp index 050fd0f50..00331f25b 100644 --- a/src/math/gfpmath/point_gfp.cpp +++ b/src/math/gfpmath/point_gfp.cpp @@ -13,295 +13,114 @@ namespace Botan { // construct the point at infinity or a random point -PointGFp::PointGFp(const CurveGFp& curve) - : mC(curve), - mX(curve.get_p(), 0), - mY(curve.get_p(), 1), - mZ(curve.get_p(), 0), - mZpow2(curve.get_p(),0), - mZpow3(curve.get_p(),0), - mAZpow4(curve.get_p(),0), - mZpow2_set(false), - mZpow3_set(false), - mAZpow4_set(false) +nPointGFp::PointGFp(const CurveGFp& curve) : + mC(curve), + mX(curve.get_p(), 0), + mY(curve.get_p(), 1), + mZ(curve.get_p(), 0) { - // first set the point wide pointer - - set_shrd_mod(mC.get_ptr_mod()); - } // construct a point given its jacobian projective coordinates PointGFp::PointGFp(const CurveGFp& curve, const GFpElement& x, - const GFpElement& y, const GFpElement& z) - : mC(curve), - mX(x), - mY(y), - mZ(z), - mZpow2(curve.get_p(),0), - mZpow3(curve.get_p(),0), - mAZpow4(curve.get_p(),0), - mZpow2_set(false), - mZpow3_set(false), - mAZpow4_set(false) - { - set_shrd_mod(mC.get_ptr_mod()); - } -PointGFp::PointGFp ( const CurveGFp& curve, const GFpElement& x, - const GFpElement& y ) - :mC(curve), - mX(x), - mY(y), - mZ(curve.get_p(),1), - mZpow2(curve.get_p(),0), - mZpow3(curve.get_p(),0), - mAZpow4(curve.get_p(),0), - mZpow2_set(false), - mZpow3_set(false), - mAZpow4_set(false) - { - set_shrd_mod(mC.get_ptr_mod()); - } - -// copy constructor -PointGFp::PointGFp(const PointGFp& other) - : mC(other.mC), - mX(other.mX), - mY(other.mY), - mZ(other.mZ), - mZpow2(other.mZpow2), - mZpow3(other.mZpow3), - mAZpow4(other.mAZpow4), - mZpow2_set(other.mZpow2_set), - mZpow3_set(other.mZpow3_set), - mAZpow4_set(other.mAZpow4_set) + const GFpElement& y, const GFpElement& z) : + mC(curve), + mX(x), + mY(y), + mZ(z) { - set_shrd_mod(mC.get_ptr_mod()); } -// assignment operator -const PointGFp& PointGFp::operator=(PointGFp const& other) +PointGFp::PointGFp(const CurveGFp& curve, + const GFpElement& x, + const GFpElement& y) : + mC(curve), + mX(x), + mY(y), + mZ(curve.get_p(),1) { - mC = other.get_curve(); - mX = other.get_jac_proj_x(); - mY = other.get_jac_proj_y(); - mZ = other.get_jac_proj_z(); - mZpow2 = GFpElement(other.mZpow2); - mZpow3 = GFpElement(other.mZpow3); - mAZpow4 = GFpElement(other.mAZpow4); - mZpow2_set = other.mZpow2_set; - mZpow3_set = other.mZpow3_set; - mAZpow4_set = other.mAZpow4_set; - set_shrd_mod(mC.get_ptr_mod()); - return *this; - } - -const PointGFp& PointGFp::assign_within_same_curve(PointGFp const& other) - { - mX = other.get_jac_proj_x(); - mY = other.get_jac_proj_y(); - mZ = other.get_jac_proj_z(); - mZpow2_set = false; - mZpow3_set = false; - mAZpow4_set = false; - // the rest stays! - return *this; - } - -void PointGFp::set_shrd_mod(std::shared_ptr<GFpModulus> p_mod) - { - mX.set_shrd_mod(p_mod); - mY.set_shrd_mod(p_mod); - mZ.set_shrd_mod(p_mod); - mZpow2.set_shrd_mod(p_mod); - mZpow3.set_shrd_mod(p_mod); - mAZpow4.set_shrd_mod(p_mod); - } - -void PointGFp::ensure_worksp() const - { - if (mp_worksp_gfp_el.get() != 0) - { - if ((*mp_worksp_gfp_el).size() == GFPEL_WKSP_SIZE) - { - return; - } - else - { - throw Invalid_State("encountered incorrect size for PointGFp´s GFpElement workspace"); - } - } - - mp_worksp_gfp_el = std::shared_ptr<std::vector<GFpElement> >(new std::vector<GFpElement>); - mp_worksp_gfp_el->reserve(9); - for (u32bit i=0; i<GFPEL_WKSP_SIZE; i++) - { - mp_worksp_gfp_el->push_back(GFpElement(1,0)); - - } } // arithmetic operators PointGFp& PointGFp::operator+=(const PointGFp& rhs) { - if (is_zero()) + if(is_zero()) { *this = rhs; return *this; } - if (rhs.is_zero()) + if(rhs.is_zero()) { return *this; } - ensure_worksp(); - - if (rhs.mZ == *(mC.get_mres_one())) - { - //U1 = mX; - (*mp_worksp_gfp_el)[0].share_assign(mX); - - //S1 = mY; - (*mp_worksp_gfp_el)[2].share_assign(mY); - } - else - { - if ((!rhs.mZpow2_set) || (!rhs.mZpow3_set)) - { - rhs.mZpow2 = rhs.mZ; - rhs.mZpow2 *= rhs.mZ; - rhs.mZpow3 = rhs.mZpow2; - rhs.mZpow3 *= rhs.mZ; - - rhs.mZpow2_set = true; - rhs.mZpow3_set = true; - } - //U1 = mX * rhs.mZpow2; - (*mp_worksp_gfp_el)[0].share_assign(mX); - (*mp_worksp_gfp_el)[0] *= rhs.mZpow2; - //S1 = mY * rhs.mZpow3; - (*mp_worksp_gfp_el)[2].share_assign(mY); - (*mp_worksp_gfp_el)[2] *= rhs.mZpow3; + GFpElement U1 = mX; + GFpElement S1 = mY; - } - if (mZ == *(mC.get_mres_one())) + if(rhs.mZ != mC.get_mres_one()) { - //U2 = rhs.mX; - (*mp_worksp_gfp_el)[1].share_assign(rhs.mX); + GFpElement rhs_z2 = rhs.mZ * rhs.mZ; - //S2 = rhs.mY; - (*mp_worksp_gfp_el)[3].share_assign(rhs.mY); + U1 *= rhs_z2; + S1 *= rhs_z2 * rhs.mZ; } - else - { - if ((!mZpow2_set) || (!mZpow3_set)) - { - // precomputation can´t be used, because *this changes anyway - mZpow2 = mZ; - mZpow2 *= mZ; - mZpow3 = mZpow2; - mZpow3 *= mZ; - } - //U2 = rhs.mX * mZpow2; - (*mp_worksp_gfp_el)[1].share_assign(rhs.mX); - (*mp_worksp_gfp_el)[1] *= mZpow2; + GFpElement U2 = rhs.mX; + GFpElement S2 = rhs.mY; - //S2 = rhs.mY * mZpow3; - (*mp_worksp_gfp_el)[3].share_assign(rhs.mY); - (*mp_worksp_gfp_el)[3] *= mZpow3; + if(mZ != mC.get_mres_one()) + { + GFpElement lhs_z2 = mZ * mZ; + U2 *= lhs_z2; + S2 *= lhs_z2 * mZ; } - //GFpElement H(U2 - U1); - - (*mp_worksp_gfp_el)[4].share_assign((*mp_worksp_gfp_el)[1]); - (*mp_worksp_gfp_el)[4] -= (*mp_worksp_gfp_el)[0]; - //GFpElement r(S2 - S1); - (*mp_worksp_gfp_el)[5].share_assign((*mp_worksp_gfp_el)[3]); - (*mp_worksp_gfp_el)[5] -= (*mp_worksp_gfp_el)[2]; - - //if(H.is_zero()) - if ((*mp_worksp_gfp_el)[4].is_zero()) + GFpElement H(U2 - U1); + GFpElement r(S2 - S1); + if(H.is_zero()) { - if ((*mp_worksp_gfp_el)[5].is_zero()) - + if(r.is_zero()) { mult2_in_place(); return *this; } + *this = PointGFp(mC); // setting myself to zero return *this; } - //U2 = H * H; - (*mp_worksp_gfp_el)[1].share_assign((*mp_worksp_gfp_el)[4]); - (*mp_worksp_gfp_el)[1] *= (*mp_worksp_gfp_el)[4]; + U2 = H * H; - //S2 = U2 * H; - (*mp_worksp_gfp_el)[3].share_assign((*mp_worksp_gfp_el)[1]); - (*mp_worksp_gfp_el)[3] *= (*mp_worksp_gfp_el)[4]; + S2 = U2 * H; - //U2 *= U1; - (*mp_worksp_gfp_el)[1] *= (*mp_worksp_gfp_el)[0]; + U2 *= U1; - //GFpElement x(r*r - S2 - (U2+U2)); - (*mp_worksp_gfp_el)[6].share_assign((*mp_worksp_gfp_el)[5]); - (*mp_worksp_gfp_el)[6] *= (*mp_worksp_gfp_el)[5]; - (*mp_worksp_gfp_el)[6] -= (*mp_worksp_gfp_el)[3]; - (*mp_worksp_gfp_el)[6] -= (*mp_worksp_gfp_el)[1]; - (*mp_worksp_gfp_el)[6] -= (*mp_worksp_gfp_el)[1]; + GFpElement x(r*r - S2 - (U2+U2)); - //GFpElement z(S1 * S2); - (*mp_worksp_gfp_el)[8].share_assign((*mp_worksp_gfp_el)[2]); - (*mp_worksp_gfp_el)[8] *= (*mp_worksp_gfp_el)[3]; + GFpElement z(S1 * S2); - //GFpElement y(r * (U2-x) - z); - (*mp_worksp_gfp_el)[7].share_assign((*mp_worksp_gfp_el)[1]); - (*mp_worksp_gfp_el)[7] -= (*mp_worksp_gfp_el)[6]; - (*mp_worksp_gfp_el)[7] *= (*mp_worksp_gfp_el)[5]; - (*mp_worksp_gfp_el)[7] -= (*mp_worksp_gfp_el)[8]; + GFpElement y(r * (U2-x) - z); - if (mZ == *(mC.get_mres_one())) + if(mZ == mC.get_mres_one()) { - if (rhs.mZ != *(mC.get_mres_one())) - { - //z = rhs.mZ * H; - (*mp_worksp_gfp_el)[8].share_assign(rhs.mZ); - (*mp_worksp_gfp_el)[8] *= (*mp_worksp_gfp_el)[4]; - } + if(rhs.mZ != mC.get_mres_one()) + z = rhs.mZ * H; else - { - //z = H; - (*mp_worksp_gfp_el)[8].share_assign((*mp_worksp_gfp_el)[4]); - } + z = H; } - else if (rhs.mZ != *(mC.get_mres_one())) + else if(rhs.mZ != mC.get_mres_one()) { - //U1 = mZ * rhs.mZ; - (*mp_worksp_gfp_el)[0].share_assign(mZ); - (*mp_worksp_gfp_el)[0] *= rhs.mZ; - - //z = U1 * H; - (*mp_worksp_gfp_el)[8].share_assign((*mp_worksp_gfp_el)[0]); - (*mp_worksp_gfp_el)[8] *= (*mp_worksp_gfp_el)[4]; - + U1 = mZ * rhs.mZ; + z = U1 * H; } else - { - //z = mZ * H; - (*mp_worksp_gfp_el)[8].share_assign(mZ); - (*mp_worksp_gfp_el)[8] *= (*mp_worksp_gfp_el)[4]; + z = mZ * H; - } - mZpow2_set = false; - mZpow3_set = false; - mAZpow4_set = false; - - mX = (*mp_worksp_gfp_el)[6]; - mY = (*mp_worksp_gfp_el)[7]; - mZ = (*mp_worksp_gfp_el)[8]; + mX = x; + mY = y; + mZ = z; return *this; @@ -310,7 +129,7 @@ PointGFp& PointGFp::operator-=(const PointGFp& rhs) { PointGFp minus_rhs = PointGFp(rhs).negate(); - if (is_zero()) + if(is_zero()) { *this = minus_rhs; } @@ -336,92 +155,39 @@ PointGFp& PointGFp::mult_this_secure(const BigInt& scalar, // use montgomery mult. in this operation this->turn_on_sp_red_mul(); - std::shared_ptr<PointGFp> H(new PointGFp(this->mC)); - std::shared_ptr<PointGFp> tmp; // used for AADA + PointGFp H(mC); PointGFp P(*this); BigInt m(scalar); - if (m < BigInt(0)) + if(m < BigInt(0)) { m = -m; P.negate(); } - if (P.is_zero() || (m == BigInt(0))) + if(P.is_zero() || (m == BigInt(0))) { - *this = *H; + *this = H; return *this; } - if (m == BigInt(1)) - { + if(m == BigInt(1)) return *this; - } - // -#ifdef CM_AADA -#ifndef CM_RAND_EXP - int max_secr_bits = max_secr.bits(); -#endif -#endif - - int mul_bits = m.bits(); // this is used for a determined number of loop runs in - // the mult_loop where leading zero´s are padded if necessary. - // Here we assign the value that will be used when no countermeasures are specified -#ifdef CM_RAND_EXP - u32bit rand_r_bit_len = 20; // Coron(99) proposes 20 bit for r -#ifdef CM_AADA + int mul_bits = m.bits(); - BigInt r_max(1); - -#endif // CM_AADA - - // use randomized exponent -#ifdef TA_COLL_T - static BigInt r_randexp; - if (new_rand) + for(int i = mul_bits - 1; i >= 0; i--) { - r_randexp = random_integer(rand_r_bit_len); - } - //assert(!r_randexp.is_zero()); -#else - BigInt r_randexp(random_integer(rand_r_bit_len)); -#endif - - m += r_randexp * point_order; - // determine mul_bits... -#ifdef CM_AADA - // AADA with rand. Exp. - //assert(rand_r_bit_len > 0); - r_max <<= rand_r_bit_len; - r_max -= 1; - //assert(r_max.bits() == rand_r_bit_len); - mul_bits = (max_secr + point_order * r_max).bits(); -#else - // rand. Exp. without AADA - mul_bits = m.bits(); -#endif // CM_AADA - - -#endif // CM_RAND_EXP - - // determine mul_bits... -#if (CM_AADA == 1 && CM_RAND_EXP != 1) - - mul_bits = max_secr_bits; -#endif // CM_AADA without CM_RAND_EXP - - //assert(mul_bits != 0); + H.mult2_in_place(); + if(m.get_bit(i)) + H += P; + } - H = mult_loop(mul_bits-1, m, H, tmp, P); + if(!H.is_zero()) // cannot convert if H == O + *this = H.get_z_to_one(); + else + *this = H; - if (!H->is_zero()) // cannot convert if H == O - { - *this = H->get_z_to_one(); - }else - { - *this = *H; - } mX.turn_off_sp_red_mul(); mY.turn_off_sp_red_mul(); mZ.turn_off_sp_red_mul(); @@ -439,226 +205,100 @@ PointGFp& PointGFp::operator*=(const BigInt& scalar) PointGFp P(*this); P.turn_on_sp_red_mul(); BigInt m(scalar); - if (m < BigInt(0)) + + if(m < BigInt(0)) { m = -m; P.negate(); } - if (P.is_zero() || (m == BigInt(0))) + + if(P.is_zero() || (m == BigInt(0))) { *this = H; return *this; } - if (m == BigInt(1)) - { - //*this == P already + + if(m == BigInt(1)) //*this == P already return *this; - } const int l = m.bits() - 1; - for (int i=l; i >=0; i--) + for(int i = l; i >= 0; --i) { - H.mult2_in_place(); - if (m.get_bit(i)) - { + if(m.get_bit(i)) H += P; - } } - if (!H.is_zero()) // cannot convert if H == O - { + if(!H.is_zero()) // cannot convert if H == O *this = H.get_z_to_one(); - }else - { + else *this = H; - } - return *this; - } - -inline std::shared_ptr<PointGFp> PointGFp::mult_loop(int l, - const BigInt& m, - std::shared_ptr<PointGFp> H, - std::shared_ptr<PointGFp> tmp, - const PointGFp& P) - { - //assert(l >= (int)m.bits()- 1); - tmp = H; - std::shared_ptr<PointGFp> to_add(new PointGFp(P)); // we just need some point - // so that we can use op= - // inside the loop - for (int i=l; i >=0; i--) - { - H->mult2_in_place(); - -#ifndef CM_AADA - - if (m.get_bit(i)) - { - *H += P; - } -#else // (CM_AADA is in) - - if (H.get() == to_add.get()) - { - to_add = tmp; // otherwise all pointers might point to the same object - // and we always need two objects to be able to switch around - } - to_add->assign_within_same_curve(*H); - tmp = H; - *tmp += P; // tmp already points to H - - if (m.get_bit(i)) - { - H = tmp; // NOTE: assign the pointer, not the value! - // (so that the operation is fast and thus as difficult - // to detect as possible) - } - else - { - H = to_add; // NOTE: this is necessary, because the assignment - // "*tmp = ..." already changed what H pointed to - - - } -#endif // CM_AADA - } - return H; + return *this; } PointGFp& PointGFp::negate() { - if (!is_zero()) - { + if(!is_zero()) mY.negate(); - } + return *this; } // *this *= 2 PointGFp& PointGFp::mult2_in_place() { - if (is_zero()) - { + if(is_zero()) return *this; - } - if (mY.is_zero()) + else if(mY.is_zero()) { - *this = PointGFp(mC); // setting myself to zero return *this; } - ensure_worksp(); - (*mp_worksp_gfp_el)[0].share_assign(mY); - (*mp_worksp_gfp_el)[0] *= mY; + GFpElement Y_squared = mY*mY; - //GFpElement S(mX * z); - (*mp_worksp_gfp_el)[1].share_assign(mX); - (*mp_worksp_gfp_el)[1] *= (*mp_worksp_gfp_el)[0]; + GFpElement S = mX * Y_squared; - //GFpElement x(S + S); - (*mp_worksp_gfp_el)[2].share_assign((*mp_worksp_gfp_el)[1]); - (*mp_worksp_gfp_el)[2] += (*mp_worksp_gfp_el)[1]; + GFpElement x = S + S; - //S = x + x; - (*mp_worksp_gfp_el)[1].share_assign((*mp_worksp_gfp_el)[2]); - (*mp_worksp_gfp_el)[1] += (*mp_worksp_gfp_el)[2]; + S = x + x; - if (!mAZpow4_set) + GFpElement a_z4 = mC.get_mres_a(); + if(mZ != mC.get_mres_one()) { - if (mZ == *(mC.get_mres_one())) - { - mAZpow4 = mC.get_mres_a(); - mAZpow4_set = true; - } - else - { - if (!mZpow2_set) - { - mZpow2 = mZ; - mZpow2 *= mZ; + GFpElement z2 = mZ * mZ; + a_z4 *= z2; + a_z4 *= z2; + } - mZpow2_set = true; - } - //x = mZpow2 * mZpow2; - (*mp_worksp_gfp_el)[2].share_assign(mZpow2); - (*mp_worksp_gfp_el)[2] *= mZpow2; + GFpElement y(mX * mX); - //mAZpow4 = mC.get_mres_a() * x; - mAZpow4 = mC.get_mres_a(); - mAZpow4 *= (*mp_worksp_gfp_el)[2]; + GFpElement M(y + y + y + a_z4); - } + x = M * M - (S+S); - } + y = Y_squared * Y_squared; - //GFpElement y(mX * mX); - (*mp_worksp_gfp_el)[3].share_assign(mX); - (*mp_worksp_gfp_el)[3] *= mX; - - //GFpElement M(y + y + y + mAZpow4); - (*mp_worksp_gfp_el)[4].share_assign((*mp_worksp_gfp_el)[3]); - (*mp_worksp_gfp_el)[4] += (*mp_worksp_gfp_el)[3]; - (*mp_worksp_gfp_el)[4] += (*mp_worksp_gfp_el)[3]; - (*mp_worksp_gfp_el)[4] += mAZpow4; - - //x = M * M - (S+S); - (*mp_worksp_gfp_el)[2].share_assign((*mp_worksp_gfp_el)[4]); - (*mp_worksp_gfp_el)[2] *= (*mp_worksp_gfp_el)[4]; - (*mp_worksp_gfp_el)[2] -= (*mp_worksp_gfp_el)[1]; - (*mp_worksp_gfp_el)[2] -= (*mp_worksp_gfp_el)[1]; - - //y = z * z; - (*mp_worksp_gfp_el)[3].share_assign((*mp_worksp_gfp_el)[0]); - (*mp_worksp_gfp_el)[3] *= (*mp_worksp_gfp_el)[0]; - - //GFpElement U(y + y); - (*mp_worksp_gfp_el)[5].share_assign((*mp_worksp_gfp_el)[3]); - (*mp_worksp_gfp_el)[5] += (*mp_worksp_gfp_el)[3]; - - //z = U + U; - (*mp_worksp_gfp_el)[0].share_assign((*mp_worksp_gfp_el)[5]); - (*mp_worksp_gfp_el)[0] += (*mp_worksp_gfp_el)[5]; - - //U = z + z; - (*mp_worksp_gfp_el)[5].share_assign((*mp_worksp_gfp_el)[0]); - (*mp_worksp_gfp_el)[5] += (*mp_worksp_gfp_el)[0]; - - //y = M * (S - x) - U; - (*mp_worksp_gfp_el)[3].share_assign((*mp_worksp_gfp_el)[1]); - (*mp_worksp_gfp_el)[3] -= (*mp_worksp_gfp_el)[2]; - (*mp_worksp_gfp_el)[3] *= (*mp_worksp_gfp_el)[4]; - (*mp_worksp_gfp_el)[3] -= (*mp_worksp_gfp_el)[5]; - - if (mZ != *(mC.get_mres_one())) - { - //z = mY * mZ; - (*mp_worksp_gfp_el)[0].share_assign(mY); - (*mp_worksp_gfp_el)[0] *= mZ; + GFpElement U(y + y); - } + GFpElement z = U + U; + + U = z + z; + + y = M * (S - x) - U; + + if(mZ != mC.get_mres_one()) + z = mY * mZ; else - { - //z = mY; - (*mp_worksp_gfp_el)[0].share_assign(mY); + z = mY; + + z = z + z; + + mX = x; + mY = y; + mZ = z; - } - //z = z + z; - (*mp_worksp_gfp_el)[6].share_assign((*mp_worksp_gfp_el)[0]); - (*mp_worksp_gfp_el)[0] += (*mp_worksp_gfp_el)[6]; - - //mX = x; - //mY = y; - //mZ = z; - mX = (*mp_worksp_gfp_el)[2]; - mY = (*mp_worksp_gfp_el)[3]; - mZ = (*mp_worksp_gfp_el)[0]; - - mZpow2_set = false; - mZpow3_set = false; - mAZpow4_set = false; return *this; } @@ -676,19 +316,14 @@ void PointGFp::turn_on_sp_red_mul() const mX.get_mres(); mY.get_mres(); mZ.get_mres(); - - mZpow2.turn_on_sp_red_mul(); - mZpow3.turn_on_sp_red_mul(); - mAZpow4.turn_on_sp_red_mul(); } -// getters /** * returns a point equivalent to *this but were * Z has value one, i.e. x and y correspond to * their values in affine coordinates */ -PointGFp const PointGFp::get_z_to_one() const +PointGFp PointGFp::get_z_to_one() const { return PointGFp(*this).set_z_to_one(); } @@ -701,7 +336,7 @@ PointGFp const PointGFp::get_z_to_one() const */ const PointGFp& PointGFp::set_z_to_one() const { - if (!(mZ.get_value() == BigInt(1)) && !(mZ.get_value() == BigInt(0))) + if(!(mZ.get_value() == BigInt(1)) && !(mZ.get_value() == BigInt(0))) { GFpElement z = inverse(mZ); GFpElement z2 = z * z; @@ -714,7 +349,7 @@ const PointGFp& PointGFp::set_z_to_one() const } else { - if (mZ.get_value() == BigInt(0)) + if(mZ.get_value() == BigInt(0)) { throw Illegal_Transformation("cannot convert Z to one"); } @@ -722,58 +357,35 @@ const PointGFp& PointGFp::set_z_to_one() const return *this; // mZ = 1 already } -const CurveGFp PointGFp::get_curve() const +GFpElement PointGFp::get_affine_x() const { - return mC; - } - -GFpElement const PointGFp::get_affine_x() const - { - - if (is_zero()) - { + if(is_zero()) throw Illegal_Transformation("cannot convert to affine"); - } - /*if(!mZpow2_set) - {*/ - mZpow2 = mZ * mZ; - mZpow2_set = true; - //} - //assert(mZpow2 == mZ*mZ); - GFpElement z2 = mZpow2; + GFpElement z2 = mZ * mZ; return mX * z2.inverse_in_place(); } -GFpElement const PointGFp::get_affine_y() const +GFpElement PointGFp::get_affine_y() const { - - if (is_zero()) - { + if(is_zero()) throw Illegal_Transformation("cannot convert to affine"); - } - /*if(!mZpow3_set ) - {*/ - mZpow3 = mZ * mZ * mZ; - mZpow3_set = true; - //} - //assert(mZpow3 == mZ * mZ *mZ); - GFpElement z3 = mZpow3; + GFpElement z3 = mZ * mZ * mZ; return mY * z3.inverse_in_place(); } -GFpElement const PointGFp::get_jac_proj_x() const +GFpElement PointGFp::get_jac_proj_x() const { return GFpElement(mX); } -GFpElement const PointGFp::get_jac_proj_y() const +GFpElement PointGFp::get_jac_proj_y() const { return GFpElement(mY); } -GFpElement const PointGFp::get_jac_proj_z() const +GFpElement PointGFp::get_jac_proj_z() const { return GFpElement(mZ); } @@ -794,14 +406,14 @@ bool PointGFp::is_zero() const void PointGFp::check_invariants() const { - if (is_zero()) + if(is_zero()) { return; } const GFpElement y2 = mY * mY; const GFpElement x3 = mX * mX * mX; - if (mZ.get_value() == BigInt(1)) + if(mZ.get_value() == BigInt(1)) { GFpElement ax = mC.get_a() * mX; if(y2 != (x3 + ax + mC.get_b())) @@ -811,16 +423,13 @@ void PointGFp::check_invariants() const } - mZpow2 = mZ * mZ; - mZpow2_set = true; - mZpow3 = mZpow2 * mZ; - mZpow3_set = true; - mAZpow4 = mZpow3 * mZ * mC.get_a(); - mAZpow4_set = true; - const GFpElement aXZ4 = mAZpow4 * mX; - const GFpElement bZ6 = mC.get_b() * mZpow3 * mZpow3; + GFpElement Zpow2 = mZ * mZ; + GFpElement Zpow3 = Zpow2 * mZ; + GFpElement AZpow4 = Zpow3 * mZ * mC.get_a(); + const GFpElement aXZ4 = AZpow4 * mX; + const GFpElement bZ6 = mC.get_b() * Zpow3 * Zpow3; - if (y2 != (x3 + aXZ4 + bZ6)) + if(y2 != (x3 + aXZ4 + bZ6)) throw Illegal_Point(); } @@ -831,12 +440,6 @@ void PointGFp::swap(PointGFp& other) mX.swap(other.mX); mY.swap(other.mY); mZ.swap(other.mZ); - mZpow2.swap(other.mZpow2); - mZpow3.swap(other.mZpow3); - mAZpow4.swap(other.mAZpow4); - std::swap<bool>(mZpow2_set, other.mZpow2_set); - std::swap<bool>(mZpow3_set, other.mZpow3_set); - std::swap<bool>(mAZpow4_set, other.mAZpow4_set); } PointGFp mult2(const PointGFp& point) @@ -846,11 +449,11 @@ PointGFp mult2(const PointGFp& point) bool operator==(const PointGFp& lhs, PointGFp const& rhs) { - if (lhs.is_zero() && rhs.is_zero()) + if(lhs.is_zero() && rhs.is_zero()) { return true; } - if ((lhs.is_zero() && !rhs.is_zero()) || (!lhs.is_zero() && rhs.is_zero())) + if((lhs.is_zero() && !rhs.is_zero()) || (!lhs.is_zero() && rhs.is_zero())) { return false; } @@ -906,16 +509,16 @@ PointGFp mult_point_secure(const PointGFp& point, const BigInt& scalar, SecureVector<byte> EC2OSP(const PointGFp& point, byte format) { SecureVector<byte> result; - if (format == PointGFp::UNCOMPRESSED) + if(format == PointGFp::UNCOMPRESSED) { result = encode_uncompressed(point); } - else if (format == PointGFp::COMPRESSED) + else if(format == PointGFp::COMPRESSED) { result = encode_compressed(point); } - else if (format == PointGFp::HYBRID) + else if(format == PointGFp::HYBRID) { result = encode_hybrid(point); } @@ -929,7 +532,7 @@ SecureVector<byte> encode_compressed(const PointGFp& point) { - if (point.is_zero()) + if(point.is_zero()) { SecureVector<byte> result (1); result[0] = 0; @@ -938,7 +541,7 @@ SecureVector<byte> encode_compressed(const PointGFp& point) } u32bit l = point.get_curve().get_p().bits(); int dummy = l & 7; - if (dummy != 0) + if(dummy != 0) { l += 8 - dummy; } @@ -949,7 +552,7 @@ SecureVector<byte> encode_compressed(const PointGFp& point) SecureVector<byte> bX = BigInt::encode_1363(x, l); result.copy(1, bX.begin(), bX.size()); BigInt y = point.get_affine_y().get_value(); - if (y.get_bit(0)) + if(y.get_bit(0)) { result[0] |= 1; } @@ -959,7 +562,7 @@ SecureVector<byte> encode_compressed(const PointGFp& point) SecureVector<byte> encode_uncompressed(const PointGFp& point) { - if (point.is_zero()) + if(point.is_zero()) { SecureVector<byte> result (1); result[0] = 0; @@ -967,7 +570,7 @@ SecureVector<byte> encode_uncompressed(const PointGFp& point) } u32bit l = point.get_curve().get_p().bits(); int dummy = l & 7; - if (dummy != 0) + if(dummy != 0) { l += 8 - dummy; } @@ -986,7 +589,7 @@ SecureVector<byte> encode_uncompressed(const PointGFp& point) SecureVector<byte> encode_hybrid(const PointGFp& point) { - if (point.is_zero()) + if(point.is_zero()) { SecureVector<byte> result (1); result[0] = 0; @@ -994,7 +597,7 @@ SecureVector<byte> encode_hybrid(const PointGFp& point) } u32bit l = point.get_curve().get_p().bits(); int dummy = l & 7; - if (dummy != 0) + if(dummy != 0) { l += 8 - dummy; } @@ -1007,7 +610,7 @@ SecureVector<byte> encode_hybrid(const PointGFp& point) SecureVector<byte> bY = BigInt::encode_1363(y, l); result.copy(1, bX.begin(), bX.size()); result.copy(l+1, bY.begin(), bY.size()); - if (y.get_bit(0)) + if(y.get_bit(0)) { result[0] |= 1; } @@ -1016,7 +619,7 @@ SecureVector<byte> encode_hybrid(const PointGFp& point) PointGFp OS2ECP(MemoryRegion<byte> const& os, const CurveGFp& curve) { - if (os.size() == 1 && os[0] == 0) + if(os.size() == 1 && os[0] == 0) { return PointGFp(curve); // return zero } @@ -1038,10 +641,6 @@ PointGFp OS2ECP(MemoryRegion<byte> const& os, const CurveGFp& curve) bX = SecureVector<byte>(os.size() - 1); bX.copy(os.begin()+1, os.size()-1); - /* Problem wäre, wenn decode() das erste bit als Vorzeichen interpretiert. - *--------------------- - * AW(FS): decode() interpretiert das erste Bit nicht als Vorzeichen - */ bi_dec_x = BigInt::decode(bX, bX.size()); x = GFpElement(curve.get_p(), bi_dec_x); bool yMod2; @@ -1072,7 +671,7 @@ PointGFp OS2ECP(MemoryRegion<byte> const& os, const CurveGFp& curve) bX.copy(os.begin() + 1, l); bY.copy(os.begin()+1+l, l); yMod2 = (pc & 0x01) == 1; - if (!(PointGFp::decompress(yMod2, x, curve) == y)) + if(!(PointGFp::decompress(yMod2, x, curve) == y)) { throw Illegal_Point("error during decoding hybrid format"); } @@ -1107,7 +706,7 @@ GFpElement PointGFp::decompress(bool yMod2, const GFpElement& x, throw Illegal_Point("error during decompression"); bool zMod2 = z.get_bit(0); - if ((zMod2 && ! yMod2) || (!zMod2 && yMod2)) + if((zMod2 && ! yMod2) || (!zMod2 && yMod2)) { z = curve.get_p() - z; } diff --git a/src/math/gfpmath/point_gfp.h b/src/math/gfpmath/point_gfp.h index 10fc404bf..276635f56 100644 --- a/src/math/gfpmath/point_gfp.h +++ b/src/math/gfpmath/point_gfp.h @@ -2,7 +2,7 @@ * Arithmetic for point groups of elliptic curves over GF(p) * * (C) 2007 Martin Doering, Christoph Ludwig, Falko Strenzke -* 2008 Jack Lloyd +* 2008-2010 Jack Lloyd * * Distributed under the terms of the Botan license */ @@ -11,9 +11,6 @@ #define BOTAN_POINT_GFP_H__ #include <botan/curve_gfp.h> -#include <botan/gfp_element.h> -#include <botan/bigint.h> -#include <botan/exceptn.h> #include <vector> namespace Botan { @@ -24,7 +21,7 @@ struct BOTAN_DLL Illegal_Point : public Exception }; /** -* This class represents one point on a curve of GF(p). +* This class represents one point on a curve of GF(p) */ class BOTAN_DLL PointGFp { @@ -48,7 +45,7 @@ class BOTAN_DLL PointGFp * Construct the point O * @param curve The base curve */ - explicit PointGFp(const CurveGFp& curve); + PointGFp(const CurveGFp& curve); /** * Construct a point given its affine coordinates @@ -56,8 +53,9 @@ class BOTAN_DLL PointGFp * @param x affine x coordinate * @param y affine y coordinate */ - explicit PointGFp(const CurveGFp& curve, GFpElement const& x, - GFpElement const& y); + PointGFp(const CurveGFp& curve, + const GFpElement& x, + const GFpElement& y); /** * Construct a point given its jacobian projective coordinates @@ -66,28 +64,13 @@ class BOTAN_DLL PointGFp * @param y jacobian projective y coordinate * @param z jacobian projective y coordinate */ - explicit PointGFp(const CurveGFp& curve, GFpElement const& x, - GFpElement const& y, GFpElement const& z); - - /** - * copy constructor - * @param other the value to clone - */ - PointGFp(const PointGFp& other); - - /** - * assignment operator - * @param other The point to use as source for the assignment - */ - const PointGFp& operator=(const PointGFp& other); - - /** - * assign another point which is on the same curve as *this - * @param other The point to use as source for the assignment - */ - const PointGFp& assign_within_same_curve(const PointGFp& other); - + PointGFp(const CurveGFp& curve, + const GFpElement& x, + const GFpElement& y, + const GFpElement& z); + //PointGFp(const PointGFp& other) = default; + //PointGFp& operator=(const PointGFp& other) = default; /** * += Operator @@ -126,8 +109,7 @@ class BOTAN_DLL PointGFp */ PointGFp& mult_this_secure(const BigInt& scalar, const BigInt& point_order, - const BigInt& max_secr - ); + const BigInt& max_secr); /** * Negate internal value(*this *= -1 ) @@ -162,43 +144,43 @@ class BOTAN_DLL PointGFp * thus x and y have just the affine values. * @result *this */ - PointGFp const get_z_to_one() const; + PointGFp get_z_to_one() const; /** * Return base curve of this point * @result the curve over GF(p) of this point */ - CurveGFp const get_curve() const; + const CurveGFp& get_curve() const { return mC; } /** * get affine x coordinate * @result affine x coordinate */ - GFpElement const get_affine_x() const; + GFpElement get_affine_x() const; /** * get affine y coordinate * @result affine y coordinate */ - GFpElement const get_affine_y() const; + GFpElement get_affine_y() const; /** * get the jacobian projective x coordinate * @result jacobian projective x coordinate */ - GFpElement const get_jac_proj_x() const; + GFpElement get_jac_proj_x() const; /** * get the jacobian projective y coordinate * @result jacobian projective y coordinate */ - GFpElement const get_jac_proj_y() const; + GFpElement get_jac_proj_y() const; /** * get the jacobian projective z coordinate * @result jacobian projective z coordinate */ - GFpElement const get_jac_proj_z() const; + GFpElement get_jac_proj_z() const; /** * Is this the point at infinity? @@ -214,49 +196,19 @@ class BOTAN_DLL PointGFp */ void check_invariants() const; - /** - * swaps the states of *this and other, does not throw! + * swaps the states of *this and other, does not throw! * @param other the object to swap values with */ void swap(PointGFp& other); - /** - * Sets the shared pointer to the GFpModulus that will be - * held in *this, specifically the various members of *this. - * Warning: do not use this function unless you know in detail about - * the implications of using - * the shared GFpModulus objects! - * Do NOT spread a shared pointer to GFpModulus over different - * threads! - * @param mod a shared pointer to a GFpModulus that will - * be held in the members *this - */ - void set_shrd_mod(std::shared_ptr<GFpModulus> p_mod); - static GFpElement decompress(bool yMod2, GFpElement const& x, const CurveGFp& curve); private: - static const u32bit GFPEL_WKSP_SIZE = 9; - void ensure_worksp() const; - - inline std::shared_ptr<PointGFp> mult_loop(int l, const BigInt& m, - std::shared_ptr<PointGFp> H, - std::shared_ptr<PointGFp> tmp, - const PointGFp& P); - CurveGFp mC; mutable GFpElement mX; // NOTE: these values must be mutable (affine<->proj) mutable GFpElement mY; mutable GFpElement mZ; - mutable GFpElement mZpow2; // mZ^2 - mutable GFpElement mZpow3; // mZ^3 - mutable GFpElement mAZpow4; // mA*mZ^4 - mutable bool mZpow2_set; - mutable bool mZpow3_set; - mutable bool mAZpow4_set; - mutable std::shared_ptr<std::vector<GFpElement> > mp_worksp_gfp_el; - }; // relational operators diff --git a/src/pubkey/ec_dompar/ec_dompar.cpp b/src/pubkey/ec_dompar/ec_dompar.cpp index e05b01465..3719153f0 100644 --- a/src/pubkey/ec_dompar/ec_dompar.cpp +++ b/src/pubkey/ec_dompar/ec_dompar.cpp @@ -409,6 +409,18 @@ std::vector<std::string> get_standard_domain_parameter(const std::string& oid) return dom_par; } + if(oid == "1.2.643.2.2.35.1" || oid == "1.2.643.2.2.36.0") // GostR3410-2001-CryptoPro-A-ParamSet + { + std::vector<std::string> dom_par; + dom_par.push_back("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD97"); + dom_par.push_back("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD94"); + dom_par.push_back("166"); + dom_par.push_back("0400000000000000000000000000000000000000000000000000000000000000018D91E471E0989CDA27DF505A453F2B7635294F2DDF23E3B122ACC99C9E9F1E14"); + dom_par.push_back("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C611070995AD10045841B09B761B893"); + dom_par.push_back("1"); + return dom_par; + } + throw Invalid_Argument("No such ECC curve " + oid); } @@ -552,18 +564,16 @@ EC_Domain_Params decode_ber_ec_dompar(SecureVector<byte> const& encoded) { BER_Decoder dec(encoded); BER_Object obj = dec.get_next_object(); - ASN1_Tag tag = obj.type_tag; - std::unique_ptr<EC_Domain_Params> p_result; - if(tag == OBJECT_ID) + if(obj.type_tag == OBJECT_ID) { OID dom_par_oid; BER_Decoder(encoded).decode(dom_par_oid); return EC_Domain_Params(get_ec_dompar(dom_par_oid.as_string())); } - else if(tag == SEQUENCE) + else if(obj.type_tag == SEQUENCE) return EC_Domain_Params(decode_ber_ec_dompar_explicit(encoded)); - else if(tag == NULL_TAG) + else if(obj.type_tag == NULL_TAG) throw Decoding_Error("cannot decode ECDSA parameters that are ImplicitCA"); throw Decoding_Error("encountered unexpected when trying to decode domain parameters"); diff --git a/src/pubkey/ecc_key/ecc_key.h b/src/pubkey/ecc_key/ecc_key.h index 9d5f57d9f..ea0f3f4ad 100644 --- a/src/pubkey/ecc_key/ecc_key.h +++ b/src/pubkey/ecc_key/ecc_key.h @@ -16,6 +16,7 @@ #include <botan/ec_dompar.h> #include <botan/x509_key.h> #include <botan/pkcs8.h> +#include <memory> namespace Botan { diff --git a/src/pubkey/gost_3410/gost_3410.cpp b/src/pubkey/gost_3410/gost_3410.cpp new file mode 100644 index 000000000..c2ddabe63 --- /dev/null +++ b/src/pubkey/gost_3410/gost_3410.cpp @@ -0,0 +1,351 @@ +/* +* GOST 34.10-2001 implemenation +* (C) 2007 Falko Strenzke, FlexSecure GmbH +* Manuel Hartl, FlexSecure GmbH +* (C) 2008-2009 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include <botan/gost_3410.h> +#include <botan/numthry.h> +#include <botan/der_enc.h> +#include <botan/ber_dec.h> +#include <botan/secmem.h> +#include <botan/point_gfp.h> + +namespace Botan { + +GOST_3410_PrivateKey::GOST_3410_PrivateKey(RandomNumberGenerator& rng, + const EC_Domain_Params& dom_pars) + { + mp_dom_pars = std::auto_ptr<EC_Domain_Params>(new EC_Domain_Params(dom_pars)); + generate_private_key(rng); + + try + { + mp_public_point->check_invariants(); + } + catch(Illegal_Point& e) + { + throw Invalid_State("GOST_3410 key generation failed"); + } + } + +GOST_3410_PrivateKey::GOST_3410_PrivateKey(const EC_Domain_Params& domain, + const BigInt& x) + { + mp_dom_pars = std::auto_ptr<EC_Domain_Params>(new EC_Domain_Params(domain)); + + m_private_value = x; + mp_public_point = std::auto_ptr<PointGFp>(new PointGFp (mp_dom_pars->get_base_point())); + mp_public_point->mult_this_secure(m_private_value, + mp_dom_pars->get_order(), + mp_dom_pars->get_order()-1); + + try + { + mp_public_point->check_invariants(); + } + catch(Illegal_Point) + { + throw Invalid_State("GOST_3410 key generation failed"); + } + } + +X509_Encoder* GOST_3410_PublicKey::x509_encoder() const + { + class GOST_3410_Key_Encoder : public X509_Encoder + { + public: + AlgorithmIdentifier alg_id() const + { + key->affirm_init(); + + SecureVector<byte> params = + encode_der_ec_dompar(key->domain_parameters(), key->m_param_enc); + + return AlgorithmIdentifier(key->get_oid(), params); + } + + MemoryVector<byte> key_bits() const + { + key->affirm_init(); + + // Trust CryptoPro to come up with something obnoxious + const BigInt x = key->mp_public_point->get_affine_x().get_value(); + const BigInt y = key->mp_public_point->get_affine_y().get_value(); + + SecureVector<byte> bits(2*std::max(x.bytes(), y.bytes())); + + y.binary_encode(bits + (bits.size() / 2 - y.bytes())); + x.binary_encode(bits + (bits.size() - y.bytes())); + + return DER_Encoder().encode(bits, OCTET_STRING).get_contents(); + } + + GOST_3410_Key_Encoder(const GOST_3410_PublicKey* k): key(k) {} + private: + const GOST_3410_PublicKey* key; + }; + + return new GOST_3410_Key_Encoder(this); + } + +X509_Decoder* GOST_3410_PublicKey::x509_decoder() + { + class GOST_3410_Key_Decoder : public X509_Decoder + { + public: + void alg_id(const AlgorithmIdentifier& alg_id) + { + // Also includes hash and cipher OIDs... brilliant design guys + OID ecc_param_id; + + BER_Decoder ber(alg_id.parameters); + ber.start_cons(SEQUENCE).decode(ecc_param_id); + + EC_Domain_Params ecc_params = get_EC_Dom_Pars_by_oid(ecc_param_id.as_string()); + + key->mp_dom_pars.reset(new EC_Domain_Params(ecc_params)); + } + + void key_bits(const MemoryRegion<byte>& bits) + { + + SecureVector<byte> key_bits; + BER_Decoder ber(bits); + ber.decode(key_bits, OCTET_STRING); + + const u32bit part_size = key_bits.size() / 2; + + BigInt y(key_bits, part_size); + BigInt x(key_bits + part_size, part_size); + + const BigInt p = key->domain_parameters().get_curve().get_p(); + + key->mp_public_point.reset( + new PointGFp(key->domain_parameters().get_curve(), + GFpElement(x, p), + GFpElement(y, p))); + + key->X509_load_hook(); + } + + GOST_3410_Key_Decoder(GOST_3410_PublicKey* k): key(k) {} + private: + GOST_3410_PublicKey* key; + }; + + return new GOST_3410_Key_Decoder(this); + } + +/* +* GOST_3410_PublicKey +*/ +void GOST_3410_PublicKey::affirm_init() const // virtual + { + EC_PublicKey::affirm_init(); + } + +void GOST_3410_PublicKey::set_domain_parameters(const EC_Domain_Params& dom_pars) + { + if(mp_dom_pars.get()) + { + // they are already set, we must ensure that they are equal to the arg + if(dom_pars != *mp_dom_pars.get()) + throw Invalid_Argument("EC_PublicKey::set_domain_parameters - cannot reset to a new value"); + + return; + } + + if(m_enc_public_point.size() == 0) + throw Invalid_State("EC_PublicKey::set_domain_parameters(): encoded public point isn't set"); + + // now try to decode the public key ... + PointGFp tmp_pp(OS2ECP(m_enc_public_point, dom_pars.get_curve())); + try + { + tmp_pp.check_invariants(); + } + catch(Illegal_Point e) + { + throw Invalid_State("EC_PublicKey::set_domain_parameters(): point does not lie on provided curve"); + } + + std::auto_ptr<EC_Domain_Params> p_tmp_pars(new EC_Domain_Params(dom_pars)); + mp_public_point.reset(new PointGFp(tmp_pp)); + mp_dom_pars = p_tmp_pars; + } + +void GOST_3410_PublicKey::set_all_values(const GOST_3410_PublicKey& other) + { + m_param_enc = other.m_param_enc; + m_enc_public_point = other.m_enc_public_point; + if(other.mp_dom_pars.get()) + mp_dom_pars.reset(new EC_Domain_Params(other.domain_parameters())); + + if(other.mp_public_point.get()) + mp_public_point.reset(new PointGFp(other.public_point())); + } + +GOST_3410_PublicKey::GOST_3410_PublicKey(const GOST_3410_PublicKey& other) + : Public_Key(), + EC_PublicKey(), + PK_Verifying_wo_MR_Key() + { + set_all_values(other); + } + +const GOST_3410_PublicKey& GOST_3410_PublicKey::operator=(const GOST_3410_PublicKey& rhs) + { + set_all_values(rhs); + return *this; + } + +bool GOST_3410_PublicKey::verify(const byte msg[], u32bit msg_len, + const byte sig[], u32bit sig_len) const + { + affirm_init(); + + const BigInt& n = mp_dom_pars->get_order(); + + if(sig_len != n.bytes()*2) + return false; + + // NOTE: it is not checked whether the public point is set + if(mp_dom_pars->get_curve().get_p() == 0) + throw Internal_Error("domain parameters not set"); + + BigInt e(msg, msg_len); + + BigInt r(sig, sig_len / 2); + BigInt s(sig + sig_len / 2, sig_len / 2); + + if(r < 0 || r >= n || s < 0 || s >= n) + return false; + + e %= n; + if(e == 0) + e = 1; + + BigInt v = inverse_mod(e, n); + + BigInt z1 = (s*v) % n; + BigInt z2 = (-r*v) % n; + + PointGFp R = (z1 * mp_dom_pars->get_base_point() + z2 * *mp_public_point); + + return (R.get_affine_x().get_value() == r); + } + +GOST_3410_PublicKey::GOST_3410_PublicKey(const EC_Domain_Params& dom_par, + const PointGFp& public_point) + { + mp_dom_pars = std::auto_ptr<EC_Domain_Params>(new EC_Domain_Params(dom_par)); + mp_public_point = std::auto_ptr<PointGFp>(new PointGFp(public_point)); + m_param_enc = ENC_EXPLICIT; + } + +void GOST_3410_PublicKey::X509_load_hook() + { + EC_PublicKey::X509_load_hook(); + EC_PublicKey::affirm_init(); + } + +u32bit GOST_3410_PublicKey::max_input_bits() const + { + if(!mp_dom_pars.get()) + { + throw Invalid_State("GOST_3410_PublicKey::max_input_bits(): domain parameters not set"); + } + return mp_dom_pars->get_order().bits(); + } + +/************************* +* GOST_3410_PrivateKey +*************************/ +void GOST_3410_PrivateKey::affirm_init() const // virtual + { + EC_PrivateKey::affirm_init(); + } + +void GOST_3410_PrivateKey::PKCS8_load_hook(bool generated) + { + EC_PrivateKey::PKCS8_load_hook(generated); + EC_PrivateKey::affirm_init(); + } + +void GOST_3410_PrivateKey::set_all_values(const GOST_3410_PrivateKey& other) + { + m_private_value = other.m_private_value; + m_param_enc = other.m_param_enc; + m_enc_public_point = other.m_enc_public_point; + + if(other.mp_dom_pars.get()) + mp_dom_pars.reset(new EC_Domain_Params(other.domain_parameters())); + + if(other.mp_public_point.get()) + mp_public_point.reset(new PointGFp(other.public_point())); + } + +GOST_3410_PrivateKey::GOST_3410_PrivateKey(GOST_3410_PrivateKey const& other) + : Public_Key(), + EC_PublicKey(), + Private_Key(), + GOST_3410_PublicKey(), + EC_PrivateKey(), + PK_Signing_Key() + { + set_all_values(other); + } + +const GOST_3410_PrivateKey& GOST_3410_PrivateKey::operator=(const GOST_3410_PrivateKey& rhs) + { + set_all_values(rhs); + return *this; + } + +SecureVector<byte> +GOST_3410_PrivateKey::sign(const byte msg[], + u32bit msg_len, + RandomNumberGenerator& rng) const + { + affirm_init(); + + const BigInt& n = mp_dom_pars->get_order(); + + BigInt k; + do + k.randomize(rng, n.bits()-1); + while(k >= n); + + if(m_private_value == 0) + throw Internal_Error("GOST_3410::sign(): no private key"); + + if(n == 0) + throw Internal_Error("GOST_3410::sign(): domain parameters not set"); + + BigInt e(msg, msg_len); + + e %= n; + if(e == 0) + e = 1; + + PointGFp k_times_P(mp_dom_pars->get_base_point()); + k_times_P.mult_this_secure(k, n, n-1); + k_times_P.check_invariants(); + BigInt r = k_times_P.get_affine_x().get_value() % n; + + if(r == 0) + throw Internal_Error("GOST_3410::sign: r was zero"); + + BigInt s = (r*m_private_value + k*e) % n; + + SecureVector<byte> output(2*n.bytes()); + r.binary_encode(output + (output.size() / 2 - r.bytes())); + s.binary_encode(output + (output.size() - s.bytes())); + return output; + } + +} diff --git a/src/pubkey/gost_3410/gost_3410.h b/src/pubkey/gost_3410/gost_3410.h new file mode 100644 index 000000000..460aca9bf --- /dev/null +++ b/src/pubkey/gost_3410/gost_3410.h @@ -0,0 +1,164 @@ +/* +* GOST 34.10-2001 +* (C) 2007 Falko Strenzke, FlexSecure GmbH +* Manuel Hartl, FlexSecure GmbH +* (C) 2008-2009 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#ifndef BOTAN_GOST_3410_KEY_H__ +#define BOTAN_GOST_3410_KEY_H__ + +#include <botan/ecc_key.h> + +namespace Botan { + +/** +* This class represents GOST_3410 Public Keys. +*/ +class BOTAN_DLL GOST_3410_PublicKey : public virtual EC_PublicKey, + public PK_Verifying_wo_MR_Key + { + public: + + /** + * Get this keys algorithm name. + * @result this keys algorithm name + */ + std::string algo_name() const { return "GOST-34.10"; } + + /** + * Get the maximum number of bits allowed to be fed to this key. + * This is the bitlength of the order of the base point. + + * @result the maximum number of input bits + */ + u32bit max_input_bits() const; + + u32bit message_parts() const { return 2; } + + u32bit message_part_size() const + { return mp_dom_pars->get_order().bytes(); } + + /** + * Verify a message with this key. + * @param message the byte array containing the message + * @param mess_len the number of bytes in the message byte array + * @param signature the byte array containing the signature + * @param sig_len the number of bytes in the signature byte array + */ + bool verify(const byte message[], u32bit mess_len, + const byte signature[], u32bit sig_len) const; + + /** + * Default constructor. Use this one if you want to later fill + * this object with data from an encoded key. + */ + GOST_3410_PublicKey() {} + + /** + * Construct a public key from a given public point. + * @param dom_par the domain parameters associated with this key + * @param public_point the public point defining this key + */ + GOST_3410_PublicKey(const EC_Domain_Params& dom_par, + const PointGFp& public_point); // sets core + + GOST_3410_PublicKey const& operator=(const GOST_3410_PublicKey& rhs); + + GOST_3410_PublicKey(const GOST_3410_PublicKey& other); + + /** + * Set the domain parameters of this key. This function has to be + * used when a key encoded without domain parameters was decoded into + * this key. Otherwise it will not be able to verify a signature. + * @param dom_pars the domain_parameters associated with this key + * @throw Invalid_Argument if the point was found not to be satisfying the + * curve equation of the provided domain parameters + * or if this key already has domain parameters set + * and these are differing from those given as the parameter + */ + void set_domain_parameters(const EC_Domain_Params& dom_pars); + + /** + * Ensure that the public point and domain parameters of this key are set. + * @throw Invalid_State if either of the two data members is not set + */ + virtual void affirm_init() const; + + /** + * Get an x509_encoder that can be used to encode this key. + * @result an x509_encoder for this key + */ + X509_Encoder* x509_encoder() const; + + /** + * Get an x509_decoder that can be used to decode a stored key into + * this key. + * @result an x509_decoder for this key + */ + X509_Decoder* x509_decoder(); + + protected: + void X509_load_hook(); + void set_all_values(const GOST_3410_PublicKey& other); + }; + +/** +* This class represents GOST_3410 Private Keys +*/ +class BOTAN_DLL GOST_3410_PrivateKey : public GOST_3410_PublicKey, + public EC_PrivateKey, + public PK_Signing_Key + { + public: + /** + * Default constructor. Use this one if you want to later fill + * this object with data from an encoded key. + */ + GOST_3410_PrivateKey() {} + + /** + * Generate a new private key + * @param the domain parameters to used for this key + */ + GOST_3410_PrivateKey(RandomNumberGenerator& rng, + const EC_Domain_Params& domain); + + /** + * Load a private key + * @param domain parameters + * @param x the private key + */ + GOST_3410_PrivateKey(const EC_Domain_Params& domain, const BigInt& x); + + GOST_3410_PrivateKey(const GOST_3410_PrivateKey& other); + GOST_3410_PrivateKey const& operator=(const GOST_3410_PrivateKey& rhs); + + /** + * Sign a message with this key. + * @param message the byte array representing the message to be signed + * @param mess_len the length of the message byte array + * @result the signature + */ + + SecureVector<byte> sign(const byte message[], u32bit mess_len, + RandomNumberGenerator& rng) const; + + /** + * Make sure that the public key parts of this object are set + * (calls EC_PublicKey::affirm_init()) as well as the private key + * value. + * @throw Invalid_State if the above conditions are not satisfied + */ + virtual void affirm_init() const; + + private: + void set_all_values(const GOST_3410_PrivateKey& other); + void PKCS8_load_hook(bool = false); + }; + +} + +#endif diff --git a/src/pubkey/gost_3410/info.txt b/src/pubkey/gost_3410/info.txt new file mode 100644 index 000000000..1d6c1ed17 --- /dev/null +++ b/src/pubkey/gost_3410/info.txt @@ -0,0 +1,14 @@ +define GOST_34_10_2001 + +load_on auto + +<requires> +alloc +asn1 +ec_dompar +ecc_key +gfpmath +libstate +numbertheory +rng +</requires> diff --git a/src/pubkey/pk_algs.cpp b/src/pubkey/pk_algs.cpp index c040e006b..dd62eb5ac 100644 --- a/src/pubkey/pk_algs.cpp +++ b/src/pubkey/pk_algs.cpp @@ -23,6 +23,10 @@ #include <botan/ecdsa.h> #endif +#if defined(BOTAN_HAS_GOST_34_10_2001) + #include <botan/gost_3410.h> +#endif + #if defined(BOTAN_HAS_NYBERG_RUEPPEL) #include <botan/nr.h> #endif @@ -70,6 +74,10 @@ Public_Key* get_public_key(const std::string& alg_name) if(alg_name == "ECDSA") return new ECDSA_PublicKey; #endif +#if defined(BOTAN_HAS_GOST_34_10_2001) + if(alg_name == "GOST-34.10") return new GOST_3410_PublicKey; +#endif + return 0; } @@ -106,6 +114,10 @@ Private_Key* get_private_key(const std::string& alg_name) if(alg_name == "ECDSA") return new ECDSA_PrivateKey; #endif +#if defined(BOTAN_HAS_GOST_34_10_2001) + if(alg_name == "GOST-34.10") return new GOST_3410_PrivateKey; +#endif + return 0; } diff --git a/src/s2k/s2k.h b/src/s2k/s2k.h index f3b8b3901..db59a5fe8 100644 --- a/src/s2k/s2k.h +++ b/src/s2k/s2k.h @@ -18,9 +18,9 @@ namespace Botan { class BOTAN_DLL S2K { public: + /** - * Create a copy of this object. - * @return an auto_ptr to a copy of this object + * @return a new instance of this same algorithm */ virtual S2K* clone() const = 0; diff --git a/src/ssl/c_kex.cpp b/src/ssl/c_kex.cpp new file mode 100644 index 000000000..802946bb9 --- /dev/null +++ b/src/ssl/c_kex.cpp @@ -0,0 +1,174 @@ +/** +* Client Key Exchange Message +* (C) 2004-2008 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/tls_messages.h> +#include <botan/dh.h> +#include <botan/rsa.h> +#include <botan/rng.h> +#include <botan/look_pk.h> +#include <botan/loadstor.h> +#include <memory> + +namespace Botan { + +/** +* Create a new Client Key Exchange message +*/ +Client_Key_Exchange::Client_Key_Exchange(RandomNumberGenerator& rng, + Record_Writer& writer, + HandshakeHash& hash, + const Public_Key* pub_key, + Version_Code using_version, + Version_Code pref_version) + { + const DH_PublicKey* dh_pub = dynamic_cast<const DH_PublicKey*>(pub_key); + const RSA_PublicKey* rsa_pub = dynamic_cast<const RSA_PublicKey*>(pub_key); + + include_length = true; + + if(dh_pub) + { + DH_PrivateKey priv_key(rng, dh_pub->get_domain()); + pre_master = priv_key.derive_key(dh_pub->get_y()); + key_material = priv_key.public_value(); + } + else if(rsa_pub) + { + pre_master.resize(48); + rng.randomize(pre_master, 48); + pre_master[0] = (pref_version >> 8) & 0xFF; + pre_master[1] = (pref_version ) & 0xFF; + + std::auto_ptr<PK_Encryptor> encryptor(get_pk_encryptor(*rsa_pub, + "PKCS1v15")); + + key_material = encryptor->encrypt(pre_master, rng); + + if(using_version == SSL_V3) + include_length = false; + } + else + throw Invalid_Argument("Client_Key_Exchange: Key not RSA or DH"); + + send(writer, hash); + } + +/** +* Read a Client Key Exchange message +*/ +Client_Key_Exchange::Client_Key_Exchange(const MemoryRegion<byte>& contents, + const CipherSuite& suite, + Version_Code using_version) + { + include_length = true; + + if(using_version == SSL_V3 && + (suite.kex_type() == CipherSuite::NO_KEX || + suite.kex_type() == CipherSuite::RSA_KEX)) + include_length = false; + + deserialize(contents); + } + +/** +* Serialize a Client Key Exchange message +*/ +SecureVector<byte> Client_Key_Exchange::serialize() const + { + SecureVector<byte> buf; + + if(include_length) + { + u16bit key_size = key_material.size(); + buf.append(get_byte(0, key_size)); + buf.append(get_byte(1, key_size)); + } + buf.append(key_material); + + return buf; + } + +/** +* Deserialize a Client Key Exchange message +*/ +void Client_Key_Exchange::deserialize(const MemoryRegion<byte>& buf) + { + if(include_length) + { + if(buf.size() < 2) + throw Decoding_Error("Client_Key_Exchange: Packet corrupted"); + + u32bit size = make_u16bit(buf[0], buf[1]); + if(size + 2 != buf.size()) + throw Decoding_Error("Client_Key_Exchange: Packet corrupted"); + + key_material.set(buf + 2, size); + } + else + key_material = buf; + } + +/** +* Return the pre_master_secret +*/ +SecureVector<byte> +Client_Key_Exchange::pre_master_secret(RandomNumberGenerator& rng, + const Private_Key* priv_key, + Version_Code version) + { + const DH_PrivateKey* dh_priv = dynamic_cast<const DH_PrivateKey*>(priv_key); + const RSA_PrivateKey* rsa_priv = + dynamic_cast<const RSA_PrivateKey*>(priv_key); + + if(dh_priv) + { + try { + pre_master = dh_priv->derive_key(key_material, key_material.size()); + } + catch(std::exception& e) + { + pre_master.resize(dh_priv->public_value().size()); + rng.randomize(pre_master, pre_master.size()); + } + + return pre_master; + } + else if(rsa_priv) + { + std::auto_ptr<PK_Decryptor> decryptor(get_pk_decryptor(*rsa_priv, + "PKCS1v15")); + + try { + pre_master = decryptor->decrypt(key_material); + + if(pre_master.size() != 48 || + make_u16bit(pre_master[0], pre_master[1]) != version) + throw Decoding_Error("Client_Key_Exchange: Secret corrupted"); + } + catch(std::exception) + { + pre_master.resize(48); + rng.randomize(pre_master, pre_master.size()); + pre_master[0] = (version >> 8) & 0xFF; + pre_master[1] = (version ) & 0xFF; + } + + return pre_master; + } + else + throw Invalid_Argument("Client_Key_Exchange: Bad key for decrypt"); + } + +/** +* Return the pre_master_secret +*/ +SecureVector<byte> Client_Key_Exchange::pre_master_secret() const + { + return pre_master; + } + +} diff --git a/src/ssl/cert_req.cpp b/src/ssl/cert_req.cpp new file mode 100644 index 000000000..4431a4a39 --- /dev/null +++ b/src/ssl/cert_req.cpp @@ -0,0 +1,156 @@ +/** +* Certificate Request Message +* (C) 2004-2006 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/tls_messages.h> +#include <botan/der_enc.h> +#include <botan/ber_dec.h> +#include <botan/loadstor.h> +#include <botan/secqueue.h> + +namespace Botan { + +/** +* Create a new Certificate Request message +*/ +Certificate_Req::Certificate_Req(Record_Writer& writer, + HandshakeHash& hash, + const std::vector<X509_Certificate>& certs) + { + for(u32bit j = 0; j != certs.size(); j++) + names.push_back(certs[j].subject_dn()); + + // FIXME: should be able to choose what to ask for + types.push_back(RSA_CERT); + types.push_back(DSS_CERT); + + send(writer, hash); + } + +/** +* Serialize a Certificate Request message +*/ +SecureVector<byte> Certificate_Req::serialize() const + { + SecureVector<byte> buf; + + buf.append(types.size()); + for(u32bit j = 0; j != types.size(); j++) + buf.append(types[j]); + + DER_Encoder encoder; + for(u32bit j = 0; j != names.size(); j++) + encoder.encode(names[j]); + + SecureVector<byte> der_names = encoder.get_contents(); + u16bit names_size = der_names.size(); + + buf.append(get_byte(0, names_size)); + buf.append(get_byte(1, names_size)); + buf.append(der_names); + + return buf; + } + +/** +* Deserialize a Certificate Request message +*/ +void Certificate_Req::deserialize(const MemoryRegion<byte>& buf) + { + if(buf.size() < 4) + throw Decoding_Error("Certificate_Req: Bad certificate request"); + + u32bit types_size = buf[0]; + + if(buf.size() < types_size + 3) + throw Decoding_Error("Certificate_Req: Bad certificate request"); + + for(u32bit j = 0; j != types_size; j++) + types.push_back(static_cast<Certificate_Type>(buf[j+1])); + + u32bit names_size = make_u16bit(buf[types_size+2], buf[types_size+3]); + + if(buf.size() != names_size + types_size + 3) + throw Decoding_Error("Certificate_Req: Bad certificate request"); + + BER_Decoder decoder(buf.begin() + types_size + 3, names_size); + + while(decoder.more_items()) + { + X509_DN name; + decoder.decode(name); + names.push_back(name); + } + } + +/** +* Create a new Certificate message +*/ +Certificate::Certificate(Record_Writer& writer, + const std::vector<X509_Certificate>& cert_list, + HandshakeHash& hash) + { + certs = cert_list; + send(writer, hash); + } + +/** +* Serialize a Certificate message +*/ +SecureVector<byte> Certificate::serialize() const + { + SecureVector<byte> buf(3); + + for(u32bit j = 0; j != certs.size(); j++) + { + SecureVector<byte> raw_cert = certs[j].BER_encode(); + u32bit cert_size = raw_cert.size(); + for(u32bit j = 0; j != 3; j++) + buf.append(get_byte(j+1, cert_size)); + buf.append(raw_cert); + } + + u32bit buf_size = buf.size() - 3; + for(u32bit j = 0; j != 3; j++) + buf[j] = get_byte(j+1, buf_size); + + return buf; + } + +/** +* Deserialize a Certificate message +*/ +void Certificate::deserialize(const MemoryRegion<byte>& buf) + { + if(buf.size() < 3) + throw Decoding_Error("Certificate: Message malformed"); + + u32bit total_size = make_u32bit(0, buf[0], buf[1], buf[2]); + + SecureQueue queue; + queue.write(buf + 3, buf.size() - 3); + + if(queue.size() != total_size) + throw Decoding_Error("Certificate: Message malformed"); + + while(queue.size()) + { + if(queue.size() < 3) + throw Decoding_Error("Certificate: Message malformed"); + + byte len[3]; + queue.read(len, 3); + u32bit cert_size = make_u32bit(0, len[0], len[1], len[2]); + + u32bit original_size = queue.size(); + X509_Certificate cert(queue); + if(queue.size() + cert_size != original_size) + throw Decoding_Error("Certificate: Message malformed"); + certs.push_back(cert); + } + } + +} diff --git a/src/ssl/cert_ver.cpp b/src/ssl/cert_ver.cpp new file mode 100644 index 000000000..5ac28dd2e --- /dev/null +++ b/src/ssl/cert_ver.cpp @@ -0,0 +1,109 @@ +/** +* Certificate Verify Message +* (C) 2004-2006 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/tls_messages.h> +#include <botan/look_pk.h> +#include <botan/rsa.h> +#include <botan/dsa.h> +#include <botan/loadstor.h> +#include <memory> + +namespace Botan { + +/** +* Create a new Certificate Verify message +*/ +Certificate_Verify::Certificate_Verify(RandomNumberGenerator& rng, + Record_Writer& writer, + HandshakeHash& hash, + const Private_Key* priv_key) + { + const PK_Signing_Key* sign_key = + dynamic_cast<const PK_Signing_Key*>(priv_key); + + if(sign_key) + { + PK_Signer* signer = 0; + try + { + if(dynamic_cast<const RSA_PrivateKey*>(sign_key)) + signer = get_pk_signer(*sign_key, "EMSA3(TLS.Digest.0)"); + else if(dynamic_cast<const DSA_PrivateKey*>(sign_key)) + signer = get_pk_signer(*sign_key, "EMSA1(SHA-1)"); + else + throw Invalid_Argument("Unknown PK algo for TLS signature"); + + signature = signer->sign_message(hash.final(), rng); + delete signer; + } + catch(...) + { + delete signer; + throw; + } + + send(writer, hash); + } + } + +/** +* Serialize a Certificate Verify message +*/ +SecureVector<byte> Certificate_Verify::serialize() const + { + SecureVector<byte> buf; + + u16bit sig_len = signature.size(); + buf.append(get_byte(0, sig_len)); + buf.append(get_byte(1, sig_len)); + buf.append(signature); + + return buf; + } + +/** +* Deserialize a Certificate Verify message +*/ +void Certificate_Verify::deserialize(const MemoryRegion<byte>& buf) + { + if(buf.size() < 2) + throw Decoding_Error("Certificate_Verify: Corrupted packet"); + + u32bit sig_len = make_u16bit(buf[0], buf[1]); + if(buf.size() != 2 + sig_len) + throw Decoding_Error("Certificate_Verify: Corrupted packet"); + + signature.set(buf + 2, sig_len); + } + +/** +* Verify a Certificate Verify message +*/ +bool Certificate_Verify::verify(const X509_Certificate& cert, + HandshakeHash& hash) + { + // FIXME: duplicate of Server_Key_Exchange::verify + + std::auto_ptr<Public_Key> key(cert.subject_public_key()); + + DSA_PublicKey* dsa_pub = dynamic_cast<DSA_PublicKey*>(key.get()); + RSA_PublicKey* rsa_pub = dynamic_cast<RSA_PublicKey*>(key.get()); + + std::auto_ptr<PK_Verifier> verifier; + + if(dsa_pub) + verifier.reset(get_pk_verifier(*dsa_pub, "EMSA1(SHA-1)", DER_SEQUENCE)); + else if(rsa_pub) + verifier.reset(get_pk_verifier(*rsa_pub, "EMSA3(TLS.Digest.0)")); + else + throw Invalid_Argument("Client did not provide a RSA/DSA cert"); + + // FIXME: WRONG + return verifier->verify_message(hash.final(), signature); + } + +} diff --git a/src/ssl/finished.cpp b/src/ssl/finished.cpp new file mode 100644 index 000000000..edbd4a3fe --- /dev/null +++ b/src/ssl/finished.cpp @@ -0,0 +1,100 @@ +/** +* Finished Message +* (C) 2004-2006 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/tls_messages.h> +#include <botan/prf_tls.h> + +namespace Botan { + +/** +* Create a new Finished message +*/ +Finished::Finished(Record_Writer& writer, + Version_Code version, Connection_Side side, + const MemoryRegion<byte>& master_secret, + HandshakeHash& hash) + { + verification_data = compute_verify(master_secret, hash, side, version); + send(writer, hash); + } + +/** +* Serialize a Finished message +*/ +SecureVector<byte> Finished::serialize() const + { + return verification_data; + } + +/** +* Deserialize a Finished message +*/ +void Finished::deserialize(const MemoryRegion<byte>& buf) + { + verification_data = buf; + } + +/** +* Verify a Finished message +*/ +bool Finished::verify(const MemoryRegion<byte>& secret, Version_Code version, + const HandshakeHash& hash, Connection_Side side) + { + SecureVector<byte> computed = compute_verify(secret, hash, side, version); + if(computed == verification_data) + return true; + return false; + } + +/** +* Compute the verify_data +*/ +SecureVector<byte> Finished::compute_verify(const MemoryRegion<byte>& secret, + HandshakeHash hash, + Connection_Side side, + Version_Code version) + { + if(version == SSL_V3) + { + const byte SSL_CLIENT_LABEL[] = { 0x43, 0x4C, 0x4E, 0x54 }; + const byte SSL_SERVER_LABEL[] = { 0x53, 0x52, 0x56, 0x52 }; + + SecureVector<byte> ssl3_finished; + + if(side == CLIENT) + hash.update(SSL_CLIENT_LABEL, sizeof(SSL_CLIENT_LABEL)); + else + hash.update(SSL_SERVER_LABEL, sizeof(SSL_SERVER_LABEL)); + + return hash.final_ssl3(secret); + } + else if(version == TLS_V10) + { + const byte TLS_CLIENT_LABEL[] = { + 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x20, 0x66, 0x69, 0x6E, 0x69, + 0x73, 0x68, 0x65, 0x64 }; + + const byte TLS_SERVER_LABEL[] = { + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x66, 0x69, 0x6E, 0x69, + 0x73, 0x68, 0x65, 0x64 }; + + TLS_PRF prf; + + SecureVector<byte> input; + if(side == CLIENT) + input.append(TLS_CLIENT_LABEL, sizeof(TLS_CLIENT_LABEL)); + else + input.append(TLS_SERVER_LABEL, sizeof(TLS_SERVER_LABEL)); + input.append(hash.final()); + + return prf.derive_key(12, secret, input); + } + else + throw Invalid_Argument("Finished message: Unknown protocol version"); + } + +} diff --git a/src/ssl/handshake_hash.cpp b/src/ssl/handshake_hash.cpp new file mode 100644 index 000000000..d94fa0178 --- /dev/null +++ b/src/ssl/handshake_hash.cpp @@ -0,0 +1,60 @@ +/** +* TLS Handshake Hash +* (C) 2004-2006 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/handshake_hash.h> +#include <botan/md5.h> +#include <botan/sha160.h> +#include <memory> + +namespace Botan { + +/** +* Return a TLS Handshake Hash +*/ +SecureVector<byte> HandshakeHash::final() + { + MD5 md5; + SHA_160 sha1; + + md5.update(data); + sha1.update(data); + + return SecureVector<byte>(md5.final(), sha1.final()); + } + +/** +* Return a SSLv3 Handshake Hash +*/ +SecureVector<byte> HandshakeHash::final_ssl3(const MemoryRegion<byte>& secret) + { + const byte PAD_INNER = 0x36, PAD_OUTER = 0x5C; + + MD5 md5; + SHA_160 sha1; + + md5.update(data); + sha1.update(data); + + md5.update(secret); + sha1.update(secret); + + for(u32bit j = 0; j != 48; j++) md5.update(PAD_INNER); + for(u32bit j = 0; j != 40; j++) sha1.update(PAD_INNER); + + SecureVector<byte> inner_md5 = md5.final(), inner_sha1 = sha1.final(); + + md5.update(secret); + sha1.update(secret); + for(u32bit j = 0; j != 48; j++) md5.update(PAD_OUTER); + for(u32bit j = 0; j != 40; j++) sha1.update(PAD_OUTER); + md5.update(inner_md5); + sha1.update(inner_sha1); + + return SecureVector<byte>(md5.final(), sha1.final()); + } + +} diff --git a/src/ssl/handshake_hash.h b/src/ssl/handshake_hash.h new file mode 100644 index 000000000..cfb351765 --- /dev/null +++ b/src/ssl/handshake_hash.h @@ -0,0 +1,38 @@ +/** +* TLS Handshake Hash +* (C) 2004-2006 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#ifndef BOTAN_TLS_HANDSHAKE_HASH_H__ +#define BOTAN_TLS_HANDSHAKE_HASH_H__ + +#include <botan/secmem.h> + +namespace Botan { + +using namespace Botan; + +/** +* TLS Handshake Hash +*/ +class BOTAN_DLL HandshakeHash + { + public: + void update(const byte in[], u32bit length) + { data.append(in, length); } + void update(const MemoryRegion<byte>& in) + { update(in.begin(), in.size()); } + void update(byte in) + { update(&in, 1); } + + SecureVector<byte> final(); + SecureVector<byte> final_ssl3(const MemoryRegion<byte>&); + private: + SecureVector<byte> data; + }; + +} + +#endif diff --git a/src/ssl/handshake_state.cpp b/src/ssl/handshake_state.cpp new file mode 100644 index 000000000..314625057 --- /dev/null +++ b/src/ssl/handshake_state.cpp @@ -0,0 +1,59 @@ +/** +* TLS Handshaking +* (C) 2004-2006 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/tls_state.h> + +namespace Botan { + +/** +* Initialize the SSL/TLS Handshake State +*/ +Handshake_State::Handshake_State() + { + client_hello = 0; + server_hello = 0; + server_certs = 0; + server_kex = 0; + cert_req = 0; + server_hello_done = 0; + + client_certs = 0; + client_kex = 0; + client_verify = 0; + client_finished = 0; + server_finished = 0; + + kex_pub = 0; + kex_priv = 0; + + do_client_auth = got_client_ccs = got_server_ccs = false; + version = SSL_V3; + } + +/** +* Destroy the SSL/TLS Handshake State +*/ +Handshake_State::~Handshake_State() + { + delete client_hello; + delete server_hello; + delete server_certs; + delete server_kex; + delete cert_req; + delete server_hello_done; + + delete client_certs; + delete client_kex; + delete client_verify; + delete client_finished; + delete server_finished; + + delete kex_pub; + delete kex_priv; + } + +} diff --git a/src/ssl/hello.cpp b/src/ssl/hello.cpp new file mode 100644 index 000000000..53f680fba --- /dev/null +++ b/src/ssl/hello.cpp @@ -0,0 +1,265 @@ +/** +* TLS Hello Messages +* (C) 2004-2006 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/tls_messages.h> +#include <botan/loadstor.h> + +namespace Botan { + +/** +* Encode and send a Handshake message +*/ +void HandshakeMessage::send(Record_Writer& writer, HandshakeHash& hash) const + { + SecureVector<byte> buf = serialize(); + SecureVector<byte> send_buf(4); + + u32bit buf_size = buf.size(); + + send_buf[0] = type(); + send_buf[1] = get_byte(1, buf_size); + send_buf[2] = get_byte(2, buf_size); + send_buf[3] = get_byte(3, buf_size); + + send_buf.append(buf); + + hash.update(send_buf); + + writer.send(HANDSHAKE, send_buf, send_buf.size()); + writer.flush(); + } + +/** +* Create a new Hello Request message +*/ +Hello_Request::Hello_Request(Record_Writer& writer) + { + HandshakeHash dummy; // FIXME: *UGLY* + send(writer, dummy); + } + +/** +* Serialize a Hello Request message +*/ +SecureVector<byte> Hello_Request::serialize() const + { + return SecureVector<byte>(); + } + +/** +* Deserialize a Hello Request message +*/ +void Hello_Request::deserialize(const MemoryRegion<byte>& buf) + { + if(buf.size()) + throw Decoding_Error("Hello_Request: Must be empty, and is not"); + } + +/** +* Create a new Client Hello message +*/ +Client_Hello::Client_Hello(RandomNumberGenerator& rng, + Record_Writer& writer, const TLS_Policy* policy, + HandshakeHash& hash) + { + c_random.resize(32); + rng.randomize(c_random, c_random.size()); + + suites = policy->ciphersuites(); + comp_algos = policy->compression(); + c_version = policy->pref_version(); + + send(writer, hash); + } + +/** +* Serialize a Client Hello message +*/ +SecureVector<byte> Client_Hello::serialize() const + { + SecureVector<byte> buf; + + buf.append(static_cast<byte>(c_version >> 8)); + buf.append(static_cast<byte>(c_version )); + buf.append(c_random); + buf.append(static_cast<byte>(sess_id.size())); + buf.append(sess_id); + + u16bit suites_size = 2*suites.size(); + + buf.append(get_byte(0, suites_size)); + buf.append(get_byte(1, suites_size)); + for(u32bit j = 0; j != suites.size(); j++) + { + buf.append(get_byte(0, suites[j])); + buf.append(get_byte(1, suites[j])); + } + + buf.append(static_cast<byte>(comp_algos.size())); + for(u32bit j = 0; j != comp_algos.size(); j++) + buf.append(comp_algos[j]); + + return buf; + } + +/** +* Deserialize a Client Hello message +*/ +void Client_Hello::deserialize(const MemoryRegion<byte>& buf) + { + if(buf.size() == 0) + throw Decoding_Error("Client_Hello: Packet corrupted"); + + if(buf.size() < 41) + throw Decoding_Error("Client_Hello: Packet corrupted"); + + c_version = static_cast<Version_Code>(make_u16bit(buf[0], buf[1])); + if(c_version != SSL_V3 && c_version != TLS_V10) + throw TLS_Exception(PROTOCOL_VERSION, "Client_Hello: Bad version code"); + + c_random.set(buf + 2, 32); + + u32bit session_id_len = buf[34]; + if(session_id_len > 32 || session_id_len + 41 > buf.size()) + throw Decoding_Error("Client_Hello: Packet corrupted"); + sess_id.copy(buf + 35, session_id_len); + + u32bit offset = 2+32+1+session_id_len; + + u16bit suites_size = make_u16bit(buf[offset], buf[offset+1]); + offset += 2; + if(suites_size % 2 == 1 || offset + suites_size + 2 > buf.size()) + throw Decoding_Error("Client_Hello: Packet corrupted"); + + for(u32bit j = 0; j != suites_size; j += 2) + { + u16bit suite = make_u16bit(buf[offset+j], buf[offset+j+1]); + suites.push_back(suite); + } + offset += suites_size; + + byte comp_algo_size = buf[offset]; + offset += 1; + if(offset + comp_algo_size > buf.size()) + throw Decoding_Error("Client_Hello: Packet corrupted"); + + for(u32bit j = 0; j != comp_algo_size; j++) + comp_algos.push_back(buf[offset+j]); + } + +/** +* Check if we offered this ciphersuite +*/ +bool Client_Hello::offered_suite(u16bit ciphersuite) const + { + for(u32bit j = 0; j != suites.size(); j++) + if(suites[j] == ciphersuite) + return true; + return false; + } + +/** +* Create a new Server Hello message +*/ +Server_Hello::Server_Hello(RandomNumberGenerator& rng, + Record_Writer& writer, const TLS_Policy* policy, + const std::vector<X509_Certificate>& certs, + const Client_Hello& c_hello, Version_Code ver, + HandshakeHash& hash) + { + bool have_rsa = false, have_dsa = false; + for(u32bit j = 0; j != certs.size(); j++) + { + Public_Key* key = certs[j].subject_public_key(); + if(key->algo_name() == "RSA") have_rsa = true; + if(key->algo_name() == "DSA") have_dsa = true; + } + + suite = policy->choose_suite(c_hello.ciphersuites(), have_rsa, have_dsa); + comp_algo = policy->choose_compression(c_hello.compression_algos()); + + s_version = ver; + s_random.resize(32); + rng.randomize(s_random, s_random.size()); + + send(writer, hash); + } + +/** +* Serialize a Server Hello message +*/ +SecureVector<byte> Server_Hello::serialize() const + { + SecureVector<byte> buf; + + buf.append(static_cast<byte>(s_version >> 8)); + buf.append(static_cast<byte>(s_version )); + buf.append(s_random); + buf.append(static_cast<byte>(sess_id.size())); + buf.append(sess_id); + + buf.append(get_byte(0, suite)); + buf.append(get_byte(1, suite)); + + buf.append(comp_algo); + + return buf; + } + +/** +* Deserialize a Server Hello message +*/ +void Server_Hello::deserialize(const MemoryRegion<byte>& buf) + { + if(buf.size() < 38) + throw Decoding_Error("Server_Hello: Packet corrupted"); + + s_version = static_cast<Version_Code>(make_u16bit(buf[0], buf[1])); + if(s_version != SSL_V3 && s_version != TLS_V10) + throw TLS_Exception(PROTOCOL_VERSION, + "Server_Hello: Unsupported server version"); + + s_random.set(buf + 2, 32); + + u32bit session_id_len = buf[2+32]; + if(session_id_len > 32 || session_id_len + 38 != buf.size()) + throw Decoding_Error("Server_Hello: Packet corrupted"); + sess_id.copy(buf + 2 + 32 + 1, session_id_len); + + suite = make_u16bit(buf[2+32+1+session_id_len], + buf[2+32+1+session_id_len+1]); + comp_algo = buf[2+32+1+session_id_len+2]; + } + + +/** +* Create a new Server Hello Done message +*/ +Server_Hello_Done::Server_Hello_Done(Record_Writer& writer, + HandshakeHash& hash) + { + send(writer, hash); + } + +/** +* Serialize a Server Hello Done message +*/ +SecureVector<byte> Server_Hello_Done::serialize() const + { + return SecureVector<byte>(); + } + +/** +* Deserialize a Server Hello Done message +*/ +void Server_Hello_Done::deserialize(const MemoryRegion<byte>& buf) + { + if(buf.size()) + throw Decoding_Error("Server_Hello_Done: Must be empty, and is not"); + } + +} diff --git a/src/ssl/info.txt b/src/ssl/info.txt new file mode 100644 index 000000000..73e4207d8 --- /dev/null +++ b/src/ssl/info.txt @@ -0,0 +1 @@ +define SSL_TLS diff --git a/src/ssl/rec_read.cpp b/src/ssl/rec_read.cpp new file mode 100644 index 000000000..95059dbf2 --- /dev/null +++ b/src/ssl/rec_read.cpp @@ -0,0 +1,204 @@ +/** +* TLS Record Reading +* (C) 2004-2006 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/tls_record.h> +#include <botan/lookup.h> +#include <botan/loadstor.h> +#include <botan/internal/debug.h> + +namespace Botan { + +/** +* Record_Reader Constructor +*/ +Record_Reader::Record_Reader(Socket& sock) : socket(sock) + { + reset(); + } + +/** +* Reset the state +*/ +void Record_Reader::reset() + { + compress.reset(); + cipher.reset(); + mac.reset(); + do_compress = false; + mac_size = pad_amount = 0; + major = minor = 0; + seq_no = 0; + } + +/** +* Set the version to use +*/ +void Record_Reader::set_version(Version_Code version) + { + if(version != SSL_V3 && version != TLS_V10) + throw Invalid_Argument("Record_Reader: Invalid protocol version"); + + major = (version >> 8) & 0xFF; + minor = (version & 0xFF); + } + +/** +* Set the compression algorithm +*/ +void Record_Reader::set_compressor(Filter* compressor) + { + compress.append(compressor); + do_compress = true; + } + +/** +* Set the keys for reading +*/ +void Record_Reader::set_keys(const CipherSuite& suite, const SessionKeys& keys, + Connection_Side side) + { + cipher.reset(); + mac.reset(); + + SymmetricKey mac_key, cipher_key; + InitializationVector iv; + + if(side == CLIENT) + { + cipher_key = keys.server_cipher_key(); + iv = keys.server_iv(); + mac_key = keys.server_mac_key(); + } + else + { + cipher_key = keys.client_cipher_key(); + iv = keys.client_iv(); + mac_key = keys.client_mac_key(); + } + + const std::string cipher_algo = suite.cipher_algo(); + const std::string mac_algo = suite.mac_algo(); + + if(have_block_cipher(cipher_algo)) + { + cipher.append(get_cipher( + cipher_algo + "/CBC/NoPadding", + cipher_key, iv, DECRYPTION) + ); + pad_amount = block_size_of(cipher_algo); + } + else if(have_stream_cipher(cipher_algo)) + { + cipher.append(get_cipher(cipher_algo, cipher_key, DECRYPTION)); + pad_amount = 0; + } + else + throw Invalid_Argument("Record_Reader: Unknown cipher " + cipher_algo); + + if(have_hash(mac_algo)) + { + if(major == 3 && minor == 0) + mac.append(new MAC_Filter("SSL3-MAC(" + mac_algo + ")", mac_key)); + else + mac.append(new MAC_Filter("HMAC(" + mac_algo + ")", mac_key)); + + mac_size = output_length_of(mac_algo); + } + else + throw Invalid_Argument("Record_Reader: Unknown hash " + mac_algo); + } + +/** +* Retrieve the next record +*/ +SecureVector<byte> Record_Reader::get_record(byte& msg_type) + { + byte header[5] = { 0 }; + + u32bit got = socket.read(header, sizeof(header)); + + if(got == 0) + { + msg_type = CONNECTION_CLOSED; + return SecureVector<byte>(); + } + else if(got != sizeof(header)) + throw Decoding_Error("Record_Reader: Record truncated"); + + msg_type = header[0]; + + const u16bit version = make_u16bit(header[1], header[2]); + + if(major && (header[1] != major || header[2] != minor)) + throw TLS_Exception(PROTOCOL_VERSION, + "Record_Reader: Got unexpected version"); + + SecureVector<byte> buffer(make_u16bit(header[3], header[4])); + if(socket.read(buffer, buffer.size()) != buffer.size()) + throw Decoding_Error("Record_Reader: Record truncated"); + + if(mac_size == 0) + return buffer; + + cipher.process_msg(buffer); + SecureVector<byte> plaintext = cipher.read_all(Pipe::LAST_MESSAGE); + + u32bit pad_size = 0; + if(pad_amount) + { + byte pad_value = plaintext[plaintext.size()-1]; + pad_size = pad_value + 1; + + if(version == SSL_V3) + { + if(pad_value > pad_amount) + throw TLS_Exception(BAD_RECORD_MAC, + "Record_Reader: Bad padding"); + } + else + { + for(u32bit j = 0; j != pad_size; j++) + if(plaintext[plaintext.size()-j-1] != pad_value) + throw TLS_Exception(BAD_RECORD_MAC, + "Record_Reader: Bad padding"); + } + } + + if(plaintext.size() < mac_size + pad_size) + throw Decoding_Error("Record_Reader: Record truncated"); + + const u32bit mac_offset = plaintext.size() - (mac_size + pad_size); + SecureVector<byte> recieved_mac(plaintext.begin() + mac_offset, + mac_size); + + const u16bit plain_length = plaintext.size() - (mac_size + pad_size); + + mac.start_msg(); + for(u32bit j = 0; j != 8; j++) + mac.write(get_byte(j, seq_no)); + mac.write(msg_type); + + if(version != SSL_V3) + for(u32bit j = 0; j != 2; j++) + mac.write(get_byte(j, version)); + + for(u32bit j = 0; j != 2; j++) + mac.write(get_byte(j, plain_length)); + mac.write(plaintext, plain_length); + mac.end_msg(); + + ++seq_no; + + SecureVector<byte> computed_mac = mac.read_all(Pipe::LAST_MESSAGE); + + if(recieved_mac != computed_mac) + throw TLS_Exception(BAD_RECORD_MAC, "Record_Reader: MAC failure"); + + return SecureVector<byte>(plaintext, mac_offset); + } + +} diff --git a/src/ssl/rec_wri.cpp b/src/ssl/rec_wri.cpp new file mode 100644 index 000000000..258b4ec17 --- /dev/null +++ b/src/ssl/rec_wri.cpp @@ -0,0 +1,258 @@ +/** +* TLS Record Writing +* (C) 2004-2006 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/tls_record.h> +#include <botan/handshake_hash.h> +#include <botan/lookup.h> +#include <botan/loadstor.h> + +namespace Botan { + +/** +* Record_Writer Constructor +*/ +Record_Writer::Record_Writer(Socket& sock) : + socket(sock), buffer(DEFAULT_BUFFERSIZE) + { + reset(); + } + +/** +* Reset the state +*/ +void Record_Writer::reset() + { + compress.reset(); + cipher.reset(); + mac.reset(); + buffer.clear(); + do_compress = false; + major = minor = buf_type = 0; + pad_amount = mac_size = buf_pos = 0; + seq_no = 0; + } + +/** +* Set the version to use +*/ +void Record_Writer::set_version(Version_Code version) + { + if(version != SSL_V3 && version != TLS_V10) + throw Invalid_Argument("Record_Writer: Invalid protocol version"); + + major = (version >> 8) & 0xFF; + minor = (version & 0xFF); + } + +/** +* Set the compression algorithm +*/ +void Record_Writer::set_compressor(Filter* compressor) + { + throw TLS_Exception(INTERNAL_ERROR, "Compression not implemented (FIXME)"); + compress.append(compressor); + } + +/** +* Set the keys for writing +*/ +void Record_Writer::set_keys(const CipherSuite& suite, const SessionKeys& keys, + Connection_Side side) + { + cipher.reset(); + mac.reset(); + + SymmetricKey mac_key, cipher_key; + InitializationVector iv; + + if(side == CLIENT) + { + cipher_key = keys.client_cipher_key(); + iv = keys.client_iv(); + mac_key = keys.client_mac_key(); + } + else + { + cipher_key = keys.server_cipher_key(); + iv = keys.server_iv(); + mac_key = keys.server_mac_key(); + } + + const std::string cipher_algo = suite.cipher_algo(); + const std::string mac_algo = suite.mac_algo(); + + if(have_block_cipher(cipher_algo)) + { + cipher.append(get_cipher( + cipher_algo + "/CBC/NoPadding", + cipher_key, iv, ENCRYPTION) + ); + pad_amount = block_size_of(cipher_algo); + } + else if(have_stream_cipher(cipher_algo)) + { + cipher.append(get_cipher(cipher_algo, cipher_key, ENCRYPTION)); + pad_amount = 0; + } + else + throw Invalid_Argument("Record_Writer: Unknown cipher " + cipher_algo); + + if(have_hash(mac_algo)) + { + if(major == 3 && minor == 0) + mac.append(new MAC_Filter("SSL3-MAC(" + mac_algo + ")", mac_key)); + else + mac.append(new MAC_Filter("HMAC(" + mac_algo + ")", mac_key)); + + mac_size = output_length_of(mac_algo); + } + else + throw Invalid_Argument("Record_Writer: Unknown hash " + mac_algo); + } + +/** +* Send one or more records to the other side +*/ +void Record_Writer::send(byte type, byte input) + { + send(type, &input, 1); + } + +/** +* Send one or more records to the other side +*/ +void Record_Writer::send(byte type, const byte input[], u32bit length) + { + if(type != buf_type) + flush(); + + const u32bit BUFFER_SIZE = buffer.size(); + buf_type = type; + + // FIXME: compression right here + + buffer.copy(buf_pos, input, length); + if(buf_pos + length >= BUFFER_SIZE) + { + send_record(buf_type, buffer, length); + input += (BUFFER_SIZE - buf_pos); + length -= (BUFFER_SIZE - buf_pos); + while(length >= BUFFER_SIZE) + { + send_record(buf_type, input, BUFFER_SIZE); + input += BUFFER_SIZE; + length -= BUFFER_SIZE; + } + buffer.copy(input, length); + buf_pos = 0; + } + buf_pos += length; + } + +/** +* Split buffer into records, and send them all +*/ +void Record_Writer::flush() + { + const byte* buf_ptr = buffer.begin(); + u32bit offset = 0; + + while(offset != buf_pos) + { + u32bit record_size = buf_pos - offset; + if(record_size > MAX_PLAINTEXT_SIZE) + record_size = MAX_PLAINTEXT_SIZE; + + send_record(buf_type, buf_ptr + offset, record_size); + offset += record_size; + } + buf_type = 0; + buf_pos = 0; + } + +/** +* Encrypt and send the record +*/ +void Record_Writer::send_record(byte type, const byte buf[], u32bit length) + { + if(length >= MAX_COMPRESSED_SIZE) + throw TLS_Exception(INTERNAL_ERROR, + "Record_Writer: Compressed packet is too big"); + + if(mac_size == 0) + send_record(type, major, minor, buf, length); + else + { + mac.start_msg(); + for(u32bit j = 0; j != 8; j++) + mac.write(get_byte(j, seq_no)); + mac.write(type); + + if(major > 3 || (major == 3 && minor != 0)) + { + mac.write(major); + mac.write(minor); + } + + mac.write(get_byte(2, length)); + mac.write(get_byte(3, length)); + mac.write(buf, length); + mac.end_msg(); + + SecureVector<byte> buf_mac = mac.read_all(Pipe::LAST_MESSAGE); + + cipher.start_msg(); + cipher.write(buf, length); + cipher.write(buf_mac); + if(pad_amount) + { + u32bit pad_val = + (pad_amount - (1 + length + buf_mac.size())) % pad_amount; + + for(u32bit j = 0; j != pad_val + 1; j++) + cipher.write(pad_val); + } + cipher.end_msg(); + + SecureVector<byte> output = cipher.read_all(Pipe::LAST_MESSAGE); + + send_record(type, major, minor, output, output.size()); + + seq_no++; + } + } + +/** +* Send a final record packet +*/ +void Record_Writer::send_record(byte type, byte major, byte minor, + const byte out[], u32bit length) + { + if(length >= MAX_CIPHERTEXT_SIZE) + throw TLS_Exception(INTERNAL_ERROR, + "Record_Writer: Record is too big"); + + byte header[5] = { type, major, minor, 0 }; + for(u32bit j = 0; j != 2; j++) + header[j+3] = get_byte<u16bit>(j, length); + + // FIXME: tradoff of TCP/syscall overhead vs copy overhead + socket.write(header, 5); + socket.write(out, length); + } + +/** +* Send an alert +*/ +void Record_Writer::alert(Alert_Level level, Alert_Type type) + { + byte alert[2] = { level, type }; + send(ALERT, alert, sizeof(alert)); + flush(); + } + +} diff --git a/src/ssl/s_kex.cpp b/src/ssl/s_kex.cpp new file mode 100644 index 000000000..fd49fcb8c --- /dev/null +++ b/src/ssl/s_kex.cpp @@ -0,0 +1,195 @@ +/** +* Server Key Exchange Message +* (C) 2004-2010 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/tls_messages.h> +#include <botan/dh.h> +#include <botan/rsa.h> +#include <botan/dsa.h> +#include <botan/look_pk.h> +#include <botan/loadstor.h> +#include <memory> + +namespace Botan { + +/** +* Create a new Server Key Exchange message +*/ +Server_Key_Exchange::Server_Key_Exchange(RandomNumberGenerator& rng, + Record_Writer& writer, + const Public_Key* kex_key, + const Private_Key* priv_key, + const MemoryRegion<byte>& c_random, + const MemoryRegion<byte>& s_random, + HandshakeHash& hash) + { + const DH_PublicKey* dh_pub = dynamic_cast<const DH_PublicKey*>(kex_key); + const RSA_PublicKey* rsa_pub = dynamic_cast<const RSA_PublicKey*>(kex_key); + + if(dh_pub) + { + params.push_back(dh_pub->get_domain().get_p()); + params.push_back(dh_pub->get_domain().get_g()); + params.push_back(BigInt::decode(dh_pub->public_value())); + } + else if(rsa_pub) + { + params.push_back(rsa_pub->get_n()); + params.push_back(rsa_pub->get_e()); + } + else + throw Invalid_Argument("Bad key for TLS key exchange: not DH or RSA"); + + // FIXME: dup of stuff in cert_ver.cpp + // FIXME: it's OK for the server to be anonymous.... + const PK_Signing_Key* sign_key = + dynamic_cast<const PK_Signing_Key*>(priv_key); + + if(!sign_key) + throw Invalid_Argument("Server Kex: Private key not for signing"); + + PK_Signer* signer = 0; + try { + if(dynamic_cast<const RSA_PrivateKey*>(sign_key)) + signer = get_pk_signer(*sign_key, "EMSA3(TLS.Digest.0)"); + else if(dynamic_cast<const DSA_PrivateKey*>(sign_key)) + { + signer = get_pk_signer(*sign_key, "EMSA1(SHA-1)"); + signer->set_output_format(DER_SEQUENCE); + } + else + throw Invalid_Argument("Bad key for TLS signature: not RSA or DSA"); + + signer->update(c_random); + signer->update(s_random); + signer->update(serialize_params()); + signature = signer->signature(rng); + + delete signer; + } + catch(...) + { + delete signer; + throw; + } + + send(writer, hash); + } + +/** +* Serialize a Server Key Exchange message +*/ +SecureVector<byte> Server_Key_Exchange::serialize() const + { + SecureVector<byte> buf = serialize_params(); + u16bit sig_len = signature.size(); + buf.append(get_byte(0, sig_len)); + buf.append(get_byte(1, sig_len)); + buf.append(signature); + return buf; + } + +/** +* Serialize the ServerParams structure +*/ +SecureVector<byte> Server_Key_Exchange::serialize_params() const + { + SecureVector<byte> buf; + for(u32bit j = 0; j != params.size(); j++) + { + SecureVector<byte> param = BigInt::encode(params[j]); + u16bit param_size = param.size(); + + buf.append(get_byte(0, param_size)); + buf.append(get_byte(1, param_size)); + buf.append(param); + } + return buf; + } + +/** +* Deserialize a Server Key Exchange message +*/ +void Server_Key_Exchange::deserialize(const MemoryRegion<byte>& buf) + { + if(buf.size() < 6) + throw Decoding_Error("Server_Key_Exchange: Packet corrupted"); + + SecureVector<byte> values[4]; + u32bit so_far = 0; + + for(u32bit j = 0; j != 4; j++) + { + u16bit len = make_u16bit(buf[so_far], buf[so_far+1]); + so_far += 2; + + if(len + so_far > buf.size()) + throw Decoding_Error("Server_Key_Exchange: Packet corrupted"); + + values[j].set(buf + so_far, len); + so_far += len; + + if(j == 2 && so_far == buf.size()) + break; + } + + params.push_back(BigInt::decode(values[0])); + params.push_back(BigInt::decode(values[1])); + if(values[3].size()) + { + params.push_back(BigInt::decode(values[2])); + signature = values[3]; + } + else + signature = values[2]; + } + +/** +* Return the public key +*/ +Public_Key* Server_Key_Exchange::key() const + { + if(params.size() == 2) + return new RSA_PublicKey(params[0], params[1]); + else if(params.size() == 3) + return new DH_PublicKey(DL_Group(params[0], params[1]), params[2]); + else + throw Internal_Error("Server_Key_Exchange::key: No key set"); + } + +/** +* Verify a Server Key Exchange message +*/ +bool Server_Key_Exchange::verify(const X509_Certificate& cert, + const MemoryRegion<byte>& c_random, + const MemoryRegion<byte>& s_random) const + { + std::auto_ptr<Public_Key> key(cert.subject_public_key()); + + DSA_PublicKey* dsa_pub = dynamic_cast<DSA_PublicKey*>(key.get()); + RSA_PublicKey* rsa_pub = dynamic_cast<RSA_PublicKey*>(key.get()); + + std::auto_ptr<PK_Verifier> verifier; + + if(dsa_pub) + { + verifier.reset(get_pk_verifier(*dsa_pub, "EMSA1(SHA-1)", DER_SEQUENCE)); + verifier->set_input_format(DER_SEQUENCE); + } + else if(rsa_pub) + verifier.reset(get_pk_verifier(*rsa_pub, "EMSA3(TLS.Digest.0)")); + else + throw Invalid_Argument("Server did not provide a RSA/DSA cert"); + + SecureVector<byte> params_got = serialize_params(); + verifier->update(c_random); + verifier->update(s_random); + verifier->update(params_got); + + return verifier->check_signature(signature, signature.size()); + } + +} diff --git a/src/ssl/socket.h b/src/ssl/socket.h new file mode 100644 index 000000000..3d893ea77 --- /dev/null +++ b/src/ssl/socket.h @@ -0,0 +1,49 @@ +/** +* Socket Interface +* (C) 2004-2006 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#ifndef BOTAN_TLS_SOCKET_H__ +#define BOTAN_TLS_SOCKET_H__ + +#include <botan/types.h> +#include <string> + +namespace Botan { + +/** +* Socket Base Class +*/ +class BOTAN_DLL Socket + { + public: + virtual u32bit read(byte[], u32bit) = 0; + virtual void write(const byte[], u32bit) = 0; + + u32bit read(byte& x) { return read(&x, 1); } + void write(byte x) { write(&x, 1); } + + virtual std::string peer_id() const = 0; + + virtual void close() = 0; + + virtual ~Socket() {} + }; + +/** +* Server Socket Base Class +*/ +class BOTAN_DLL Server_Socket + { + public: + virtual Socket* accept() = 0; + virtual void close() = 0; + + virtual ~Server_Socket() {} + }; + +} + +#endif diff --git a/src/ssl/tls_alerts.h b/src/ssl/tls_alerts.h new file mode 100644 index 000000000..894bca4af --- /dev/null +++ b/src/ssl/tls_alerts.h @@ -0,0 +1,46 @@ +/** +* Alert Message +* (C) 2004-2006 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#ifndef BOTAN_TLS_ALERT_H__ +#define BOTAN_TLS_ALERT_H__ + +#include <botan/tls_exceptn.h> + +namespace Botan { + +/** +* SSL/TLS Alert Message +*/ +class BOTAN_DLL Alert + { + public: + bool is_fatal() const { return fatal; } + Alert_Type type() const { return type_code; } + + /** + * Deserialize an Alert message + */ + Alert(const MemoryRegion<byte>& buf) + { + if(buf.size() != 2) + throw Decoding_Error("Alert: Bad size for alert message"); + + if(buf[0] == 1) fatal = false; + else if(buf[0] == 2) fatal = true; + else + throw Decoding_Error("Alert: Bad type code for alert level"); + + type_code = static_cast<Alert_Type>(buf[1]); + } + private: + bool fatal; + Alert_Type type_code; + }; + +} + +#endif diff --git a/src/ssl/tls_client.cpp b/src/ssl/tls_client.cpp new file mode 100644 index 000000000..ce33573f5 --- /dev/null +++ b/src/ssl/tls_client.cpp @@ -0,0 +1,571 @@ +/** +* TLS Client +* (C) 2004-2006 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/tls_client.h> +#include <botan/tls_alerts.h> +#include <botan/tls_state.h> +#include <botan/loadstor.h> +#include <botan/rsa.h> +#include <botan/dsa.h> +#include <botan/dh.h> + +namespace Botan { + +namespace { + +// FIXME: checks are wrong for session reuse (add a flag for that) +/** +* Verify the state transition is allowed +*/ +void client_check_state(Handshake_Type new_msg, Handshake_State* state) + { + class State_Transition_Error : public Unexpected_Message + { + public: + State_Transition_Error(const std::string& err) : + Unexpected_Message("State transition error from " + err) {} + }; + + if(new_msg == HELLO_REQUEST) + { + if(state->client_hello) + throw State_Transition_Error("HelloRequest"); + } + else if(new_msg == SERVER_HELLO) + { + if(!state->client_hello || state->server_hello) + throw State_Transition_Error("ServerHello"); + } + else if(new_msg == CERTIFICATE) + { + if(!state->server_hello || state->server_kex || + state->cert_req || state->server_hello_done) + throw State_Transition_Error("ServerCertificate"); + } + else if(new_msg == SERVER_KEX) + { + if(!state->server_hello || state->server_kex || + state->cert_req || state->server_hello_done) + throw State_Transition_Error("ServerKeyExchange"); + } + else if(new_msg == CERTIFICATE_REQUEST) + { + if(!state->server_certs || state->cert_req || state->server_hello_done) + throw State_Transition_Error("CertificateRequest"); + } + else if(new_msg == SERVER_HELLO_DONE) + { + if(!state->server_hello || state->server_hello_done) + throw State_Transition_Error("ServerHelloDone"); + } + else if(new_msg == HANDSHAKE_CCS) + { + if(!state->client_finished || state->server_finished) + throw State_Transition_Error("ServerChangeCipherSpec"); + } + else if(new_msg == FINISHED) + { + if(!state->got_server_ccs) + throw State_Transition_Error("ServerFinished"); + } + else + throw Unexpected_Message("Unexpected message in handshake"); + } + +} + +/** +* TLS Client Constructor +*/ +TLS_Client::TLS_Client(RandomNumberGenerator& r, + Socket& sock, const TLS_Policy* pol) : + rng(r), writer(sock), reader(sock), policy(pol ? pol : new TLS_Policy) + { + peer_id = sock.peer_id(); + + initialize(); + } + +/** +* TLS Client Constructor +*/ +TLS_Client::TLS_Client(RandomNumberGenerator& r, + Socket& sock, const X509_Certificate& cert, + const Private_Key& key, const TLS_Policy* pol) : + rng(r), writer(sock), reader(sock), policy(pol ? pol : new TLS_Policy) + { + peer_id = sock.peer_id(); + + certs.push_back(cert); + keys.push_back(PKCS8::copy_key(key, rng)); + + initialize(); + } + +/** +* TLS Client Destructor +*/ +TLS_Client::~TLS_Client() + { + close(); + for(u32bit j = 0; j != keys.size(); j++) + delete keys[j]; + delete policy; + delete state; + } + +/** +* Initialize a TLS client connection +*/ +void TLS_Client::initialize() + { + Alert_Type error_type = NO_ALERT_TYPE; + + try { + state = 0; + active = false; + writer.set_version(policy->pref_version()); + do_handshake(); + } + catch(TLS_Exception& e) + { + error_type = e.type(); + } + catch(std::exception& e) + { + error_type = HANDSHAKE_FAILURE; + } + + if(error_type != NO_ALERT_TYPE) + { + if(active) + { + active = false; + reader.reset(); + + writer.alert(FATAL, error_type); + writer.reset(); + } + + if(state) + { + delete state; + state = 0; + } + + throw Stream_IO_Error("TLS_Client: Handshake failed"); + } + } + +/** +* Return the peer's certificate chain +*/ +std::vector<X509_Certificate> TLS_Client::peer_cert_chain() const + { + return peer_certs; + } + +/** +* Write to a TLS connection +*/ +void TLS_Client::write(const byte buf[], u32bit length) + { + if(!active) + throw TLS_Exception(INTERNAL_ERROR, + "TLS_Client::write called while closed"); + + writer.send(APPLICATION_DATA, buf, length); + } + +/** +* Read from a TLS connection +*/ +u32bit TLS_Client::read(byte out[], u32bit length) + { + if(!active) + return 0; + + writer.flush(); + + while(read_buf.size() == 0) + { + state_machine(); + if(active == false) + break; + } + + u32bit got = std::min(read_buf.size(), length); + read_buf.read(out, got); + return got; + } + +/** +* Close a TLS connection +*/ +void TLS_Client::close() + { + close(WARNING, CLOSE_NOTIFY); + } + +/** +* Check connection status +*/ +bool TLS_Client::is_closed() const + { + if(!active) + return true; + return false; + } + +/** +* Close a TLS connection +*/ +void TLS_Client::close(Alert_Level level, Alert_Type alert_code) + { + if(active) + { + try { + writer.alert(level, alert_code); + writer.flush(); + } + catch(...) {} + + active = false; + } + } + +/** +* Iterate the TLS state machine +*/ +void TLS_Client::state_machine() + { + byte rec_type; + SecureVector<byte> record = reader.get_record(rec_type); + + if(rec_type == CONNECTION_CLOSED) + { + active = false; + reader.reset(); + writer.reset(); + } + else if(rec_type == APPLICATION_DATA) + { + if(active) + read_buf.write(record, record.size()); + else + throw Unexpected_Message("Application data before handshake done"); + } + else if(rec_type == HANDSHAKE || rec_type == CHANGE_CIPHER_SPEC) + read_handshake(rec_type, record); + else if(rec_type == ALERT) + { + Alert alert(record); + + if(alert.is_fatal() || alert.type() == CLOSE_NOTIFY) + { + if(alert.type() == CLOSE_NOTIFY) + writer.alert(WARNING, CLOSE_NOTIFY); + + reader.reset(); + writer.reset(); + active = false; + if(state) + { + delete state; + state = 0; + } + } + } + else + throw Unexpected_Message("Unknown message type recieved"); + } + +/** +* Split up and process handshake messages +*/ +void TLS_Client::read_handshake(byte rec_type, + const MemoryRegion<byte>& rec_buf) + { + if(rec_type == HANDSHAKE) + state->queue.write(rec_buf, rec_buf.size()); + + while(true) + { + Handshake_Type type = HANDSHAKE_NONE; + SecureVector<byte> contents; + + if(rec_type == HANDSHAKE) + { + if(state->queue.size() >= 4) + { + byte head[4] = { 0 }; + state->queue.peek(head, 4); + + const u32bit length = make_u32bit(0, head[1], head[2], head[3]); + + if(state->queue.size() >= length + 4) + { + type = static_cast<Handshake_Type>(head[0]); + contents.resize(length); + state->queue.read(head, 4); + state->queue.read(contents, contents.size()); + } + } + } + else if(rec_type == CHANGE_CIPHER_SPEC) + { + if(state->queue.size() == 0 && rec_buf.size() == 1 && rec_buf[0] == 1) + type = HANDSHAKE_CCS; + else + throw Decoding_Error("Malformed ChangeCipherSpec message"); + } + else + throw Decoding_Error("Unknown message type in handshake processing"); + + if(type == HANDSHAKE_NONE) + break; + + process_handshake_msg(type, contents); + + if(type == HANDSHAKE_CCS || !state) + break; + } + } + +/** +* Process a handshake message +*/ +void TLS_Client::process_handshake_msg(Handshake_Type type, + const MemoryRegion<byte>& contents) + { + if(type == HELLO_REQUEST) + { + if(state == 0) + state = new Handshake_State(); + else + return; + } + + if(state == 0) + throw Unexpected_Message("Unexpected handshake message"); + + if(type != HANDSHAKE_CCS && type != HELLO_REQUEST && type != FINISHED) + { + state->hash.update(static_cast<byte>(type)); + const u32bit record_length = contents.size(); + for(u32bit j = 0; j != 3; j++) + state->hash.update(get_byte(j+1, record_length)); + state->hash.update(contents); + } + + if(type == HELLO_REQUEST) + { + client_check_state(type, state); + + Hello_Request hello_request(contents); + state->client_hello = new Client_Hello(rng, writer, policy, state->hash); + } + else if(type == SERVER_HELLO) + { + client_check_state(type, state); + + state->server_hello = new Server_Hello(contents); + + if(!state->client_hello->offered_suite( + state->server_hello->ciphersuite() + ) + ) + throw TLS_Exception(HANDSHAKE_FAILURE, + "TLS_Client: Server replied with bad ciphersuite"); + + state->version = state->server_hello->version(); + + if(state->version > state->client_hello->version()) + throw TLS_Exception(HANDSHAKE_FAILURE, + "TLS_Client: Server replied with bad version"); + + if(state->version < policy->min_version()) + throw TLS_Exception(PROTOCOL_VERSION, + "TLS_Client: Server is too old for specified policy"); + + writer.set_version(state->version); + reader.set_version(state->version); + + state->suite = CipherSuite(state->server_hello->ciphersuite()); + } + else if(type == CERTIFICATE) + { + client_check_state(type, state); + + if(state->suite.sig_type() == CipherSuite::NO_SIG) + throw Unexpected_Message("Recived certificate from anonymous server"); + + state->server_certs = new Certificate(contents); + + peer_certs = state->server_certs->cert_chain(); + if(peer_certs.size() == 0) + throw TLS_Exception(HANDSHAKE_FAILURE, + "TLS_Client: No certificates sent by server"); + + if(!policy->check_cert(peer_certs, peer_id)) + throw TLS_Exception(BAD_CERTIFICATE, + "TLS_Client: Server certificate is not valid"); + + state->kex_pub = peer_certs[0].subject_public_key(); + + bool is_dsa = false, is_rsa = false; + + if(dynamic_cast<DSA_PublicKey*>(state->kex_pub)) + is_dsa = true; + else if(dynamic_cast<RSA_PublicKey*>(state->kex_pub)) + is_rsa = true; + else + throw TLS_Exception(UNSUPPORTED_CERTIFICATE, + "Unknown key type recieved in server kex"); + + if((is_dsa && state->suite.sig_type() != CipherSuite::DSA_SIG) || + (is_rsa && state->suite.sig_type() != CipherSuite::RSA_SIG)) + throw TLS_Exception(ILLEGAL_PARAMETER, + "Certificate key type did not match ciphersuite"); + } + else if(type == SERVER_KEX) + { + client_check_state(type, state); + + if(state->suite.kex_type() == CipherSuite::NO_KEX) + throw Unexpected_Message("Unexpected key exchange from server"); + + state->server_kex = new Server_Key_Exchange(contents); + + if(state->kex_pub) + delete state->kex_pub; + + state->kex_pub = state->server_kex->key(); + + bool is_dh = false, is_rsa = false; + + if(dynamic_cast<DH_PublicKey*>(state->kex_pub)) + is_dh = true; + else if(dynamic_cast<RSA_PublicKey*>(state->kex_pub)) + is_rsa = true; + else + throw TLS_Exception(HANDSHAKE_FAILURE, + "Unknown key type recieved in server kex"); + + if((is_dh && state->suite.kex_type() != CipherSuite::DH_KEX) || + (is_rsa && state->suite.kex_type() != CipherSuite::RSA_KEX)) + throw TLS_Exception(ILLEGAL_PARAMETER, + "Certificate key type did not match ciphersuite"); + + if(state->suite.sig_type() != CipherSuite::NO_SIG) + { + if(!state->server_kex->verify(peer_certs[0], + state->client_hello->random(), + state->server_hello->random())) + throw TLS_Exception(DECRYPT_ERROR, + "Bad signature on server key exchange"); + } + } + else if(type == CERTIFICATE_REQUEST) + { + client_check_state(type, state); + + state->cert_req = new Certificate_Req(contents); + state->do_client_auth = true; + } + else if(type == SERVER_HELLO_DONE) + { + client_check_state(type, state); + + state->server_hello_done = new Server_Hello_Done(contents); + + if(state->do_client_auth) + { + std::vector<X509_Certificate> send_certs; + + std::vector<Certificate_Type> types = + state->cert_req->acceptable_types(); + + // FIXME: Fill in useful certs here, if any + state->client_certs = new Certificate(writer, send_certs, + state->hash); + } + + state->client_kex = + new Client_Key_Exchange(rng, writer, state->hash, + state->kex_pub, state->version, + state->client_hello->version()); + + if(state->do_client_auth) + { + Private_Key* key_matching_cert = 0; // FIXME + state->client_verify = new Certificate_Verify(rng, + writer, state->hash, + key_matching_cert); + } + + state->keys = SessionKeys(state->suite, state->version, + state->client_kex->pre_master_secret(), + state->client_hello->random(), + state->server_hello->random()); + + writer.send(CHANGE_CIPHER_SPEC, 1); + writer.flush(); + + writer.set_keys(state->suite, state->keys, CLIENT); + + state->client_finished = new Finished(writer, state->version, CLIENT, + state->keys.master_secret(), + state->hash); + } + else if(type == HANDSHAKE_CCS) + { + client_check_state(type, state); + + reader.set_keys(state->suite, state->keys, CLIENT); + state->got_server_ccs = true; + } + else if(type == FINISHED) + { + client_check_state(type, state); + + state->server_finished = new Finished(contents); + + if(!state->server_finished->verify(state->keys.master_secret(), + state->version, state->hash, SERVER)) + throw TLS_Exception(DECRYPT_ERROR, + "Finished message didn't verify"); + + delete state; + state = 0; + active = true; + } + else + throw Unexpected_Message("Unknown handshake message recieved"); + } + +/** +* Perform a client-side TLS handshake +*/ +void TLS_Client::do_handshake() + { + state = new Handshake_State; + + state->client_hello = new Client_Hello(rng, writer, policy, state->hash); + + while(true) + { + if(active && !state) + break; + if(!active && !state) + throw TLS_Exception(HANDSHAKE_FAILURE, "TLS_Client: Handshake failed"); + + state_machine(); + } + } + +} diff --git a/src/ssl/tls_client.h b/src/ssl/tls_client.h new file mode 100644 index 000000000..896decdf9 --- /dev/null +++ b/src/ssl/tls_client.h @@ -0,0 +1,79 @@ +/** +* TLS Client +* (C) 2004-2010 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#ifndef BOTAN_TLS_CLIENT_H__ +#define BOTAN_TLS_CLIENT_H__ + +#include <botan/tls_connection.h> +#include <botan/tls_state.h> +#include <vector> +#include <string> + +namespace Botan { + +/** +* TLS Client +*/ + +// FIXME: much of this can probably be moved up to TLS_Connection +class BOTAN_DLL TLS_Client : public TLS_Connection + { + public: + u32bit read(byte buf[], u32bit buf_len); + void write(const byte buf[], u32bit buf_len); + + std::vector<X509_Certificate> peer_cert_chain() const; + + void close(); + bool is_closed() const; + + TLS_Client(RandomNumberGenerator& rng, + Socket& peer, + const TLS_Policy* policy = 0); + +#if 0 + void add_cert(const X509_Certificate& cert, + const Private_Key& cert_key); +#endif + + // FIXME: support multiple cert/key pairs + TLS_Client(RandomNumberGenerator& rng, + Socket& peer, + const X509_Certificate& cert, + const Private_Key& cert_key, + const TLS_Policy* policy = 0); + + ~TLS_Client(); + private: + void close(Alert_Level, Alert_Type); + + void initialize(); + void do_handshake(); + + void state_machine(); + void read_handshake(byte, const MemoryRegion<byte>&); + void process_handshake_msg(Handshake_Type, const MemoryRegion<byte>&); + + RandomNumberGenerator& rng; + + Record_Writer writer; + Record_Reader reader; + const TLS_Policy* policy; + + std::vector<X509_Certificate> certs, peer_certs; + std::vector<Private_Key*> keys; + + Handshake_State* state; + SecureVector<byte> session_id; + SecureQueue read_buf; + std::string peer_id; + bool active; + }; + +} + +#endif diff --git a/src/ssl/tls_connection.h b/src/ssl/tls_connection.h new file mode 100644 index 000000000..ff55cceab --- /dev/null +++ b/src/ssl/tls_connection.h @@ -0,0 +1,36 @@ +/** +* TLS Connection +* (C) 2004-2006 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#ifndef BOTAN_TLS_CONNECTION_H__ +#define BOTAN_TLS_CONNECTION_H__ + +#include <botan/x509cert.h> +#include <vector> + +namespace Botan { + +/** +* TLS Connection +*/ +class BOTAN_DLL TLS_Connection + { + public: + virtual u32bit read(byte[], u32bit) = 0; + virtual void write(const byte[], u32bit) = 0; + u32bit read(byte& in) { return read(&in, 1); } + void write(byte out) { write(&out, 1); } + + virtual std::vector<X509_Certificate> peer_cert_chain() const = 0; + + virtual void close() = 0; + + virtual ~TLS_Connection() {} + }; + +} + +#endif diff --git a/src/ssl/tls_exceptn.h b/src/ssl/tls_exceptn.h new file mode 100644 index 000000000..3ba852875 --- /dev/null +++ b/src/ssl/tls_exceptn.h @@ -0,0 +1,43 @@ +/** +* Exceptions +* (C) 2004-2006 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#ifndef BOTAN_TLS_EXCEPTION_H__ +#define BOTAN_TLS_EXCEPTION_H__ + +#include <botan/exceptn.h> +#include <botan/tls_magic.h> + +namespace Botan { + +/** +* Exception Base Class +*/ +class BOTAN_DLL TLS_Exception : public Exception + { + public: + Alert_Type type() const throw() { return alert_type; } + + TLS_Exception(Alert_Type type, + const std::string& err_msg = "Unknown error") : + Exception(err_msg), alert_type(type) {} + + private: + Alert_Type alert_type; + }; + +/** +* Unexpected_Message Exception +*/ +struct Unexpected_Message : public TLS_Exception + { + Unexpected_Message(const std::string& err) : + TLS_Exception(UNEXPECTED_MESSAGE, err) {} + }; + +} + +#endif diff --git a/src/ssl/tls_magic.h b/src/ssl/tls_magic.h new file mode 100644 index 000000000..41fb756e9 --- /dev/null +++ b/src/ssl/tls_magic.h @@ -0,0 +1,120 @@ +/** +* SSL/TLS Protocol Constants +* (C) 2004-2006 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#ifndef BOTAN_TLS_PROTOCOL_MAGIC_H__ +#define BOTAN_TLS_PROTOCOL_MAGIC_H__ + +namespace Botan { + +/** +* Protocol Constants for SSL/TLS +*/ +enum Size_Limits { + MAX_PLAINTEXT_SIZE = 16*1024, + MAX_COMPRESSED_SIZE = MAX_PLAINTEXT_SIZE + 1024, + MAX_CIPHERTEXT_SIZE = MAX_COMPRESSED_SIZE + 1024 +}; + +enum Version_Code { + NO_VERSION_SET = 0x0000, + SSL_V3 = 0x0300, + TLS_V10 = 0x0301, + TLS_V11 = 0x0302 +}; + +enum Connection_Side { CLIENT, SERVER }; + +enum Record_Type { + CONNECTION_CLOSED = 0, + + CHANGE_CIPHER_SPEC = 20, + ALERT = 21, + HANDSHAKE = 22, + APPLICATION_DATA = 23 +}; + +enum Handshake_Type { + HELLO_REQUEST = 0, + CLIENT_HELLO = 1, + SERVER_HELLO = 2, + CERTIFICATE = 11, + SERVER_KEX = 12, + CERTIFICATE_REQUEST = 13, + SERVER_HELLO_DONE = 14, + CERTIFICATE_VERIFY = 15, + CLIENT_KEX = 16, + FINISHED = 20, + + HANDSHAKE_CCS = 100, + HANDSHAKE_NONE = 101 +}; + +enum Alert_Level { + WARNING = 1, + FATAL = 2 +}; + +enum Alert_Type { + CLOSE_NOTIFY = 0, + UNEXPECTED_MESSAGE = 10, + BAD_RECORD_MAC = 20, + DECRYPTION_FAILED = 21, + RECORD_OVERFLOW = 22, + DECOMPRESSION_FAILURE = 30, + HANDSHAKE_FAILURE = 40, + BAD_CERTIFICATE = 42, + UNSUPPORTED_CERTIFICATE = 43, + CERTIFICATE_REVOKED = 44, + CERTIFICATE_EXPIRED = 45, + CERTIFICATE_UNKNOWN = 46, + ILLEGAL_PARAMETER = 47, + UNKNOWN_CA = 48, + ACCESS_DENIED = 49, + DECODE_ERROR = 50, + DECRYPT_ERROR = 51, + EXPORT_RESTRICTION = 60, + PROTOCOL_VERSION = 70, + INSUFFICIENT_SECURITY = 71, + INTERNAL_ERROR = 80, + USER_CANCELED = 90, + NO_RENEGOTIATION = 100, + + UNKNOWN_PSK_IDENTITY = 115, + + NO_ALERT_TYPE = 0xFFFF +}; + +enum Certificate_Type { + RSA_CERT = 1, + DSS_CERT = 2, + DH_RSA_CERT = 3, + DH_DSS_CERT = 4 +}; + +enum Ciphersuite_Code { + RSA_RC4_MD5 = 0x0004, + RSA_RC4_SHA = 0x0005, + RSA_3DES_SHA = 0x000A, + RSA_AES128_SHA = 0x002F, + RSA_AES256_SHA = 0x0035, + + DHE_RSA_3DES_SHA = 0x0016, + DHE_RSA_AES128_SHA = 0x0033, + DHE_RSA_AES256_SHA = 0x0039, + + DHE_DSS_3DES_SHA = 0x0013, + DHE_DSS_AES128_SHA = 0x0032, + DHE_DSS_AES256_SHA = 0x0038 +}; + +enum Compression_Algo { + NO_COMPRESSION = 0x00 +}; + +} + +#endif diff --git a/src/ssl/tls_messages.h b/src/ssl/tls_messages.h new file mode 100644 index 000000000..4b512a963 --- /dev/null +++ b/src/ssl/tls_messages.h @@ -0,0 +1,282 @@ +/** +* TLS Messages +* (C) 2004-2010 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#ifndef BOTAN_TLS_MESSAGES_H__ +#define BOTAN_TLS_MESSAGES_H__ + +#include <botan/tls_record.h> +#include <botan/handshake_hash.h> +#include <botan/tls_policy.h> +#include <botan/bigint.h> +#include <botan/pkcs8.h> +#include <botan/x509cert.h> +#include <vector> + +namespace Botan { + +/** +* TLS Handshake Message Base Class +*/ +class BOTAN_DLL HandshakeMessage + { + public: + void send(Record_Writer&, HandshakeHash&) const; + + virtual Handshake_Type type() const = 0; + + virtual ~HandshakeMessage() {} + private: + HandshakeMessage& operator=(const HandshakeMessage&) { return (*this); } + virtual SecureVector<byte> serialize() const = 0; + virtual void deserialize(const MemoryRegion<byte>&) = 0; + }; + +/** +* Client Hello Message +*/ +class BOTAN_DLL Client_Hello : public HandshakeMessage + { + public: + Handshake_Type type() const { return CLIENT_HELLO; } + Version_Code version() const { return c_version; } + SecureVector<byte> session_id() const { return sess_id; } + std::vector<u16bit> ciphersuites() const { return suites; } + std::vector<byte> compression_algos() const { return comp_algos; } + + SecureVector<byte> random() const { return c_random; } + + bool offered_suite(u16bit) const; + + Client_Hello(RandomNumberGenerator& rng, + Record_Writer&, const TLS_Policy*, HandshakeHash&); + + Client_Hello(const MemoryRegion<byte>& buf) { deserialize(buf); } + private: + SecureVector<byte> serialize() const; + void deserialize(const MemoryRegion<byte>&); + + Version_Code c_version; + SecureVector<byte> sess_id, c_random; + std::vector<u16bit> suites; + std::vector<byte> comp_algos; + }; + +/** +* Client Key Exchange Message +*/ +class BOTAN_DLL Client_Key_Exchange : public HandshakeMessage + { + public: + Handshake_Type type() const { return CLIENT_KEX; } + + SecureVector<byte> pre_master_secret() const; + + SecureVector<byte> pre_master_secret(RandomNumberGenerator& rng, + const Private_Key* key, + Version_Code version); + + Client_Key_Exchange(RandomNumberGenerator& rng, + Record_Writer& output, + HandshakeHash& hash, + const Public_Key* my_key, + Version_Code using_version, + Version_Code pref_version); + + Client_Key_Exchange(const MemoryRegion<byte>& buf, + const CipherSuite& suite, + Version_Code using_version); + private: + SecureVector<byte> serialize() const; + void deserialize(const MemoryRegion<byte>&); + + SecureVector<byte> key_material, pre_master; + bool include_length; + }; + +/** +* Certificate Message +*/ +class BOTAN_DLL Certificate : public HandshakeMessage + { + public: + Handshake_Type type() const { return CERTIFICATE; } + std::vector<X509_Certificate> cert_chain() const { return certs; } + + Certificate(Record_Writer&, const std::vector<X509_Certificate>&, + HandshakeHash&); + Certificate(const MemoryRegion<byte>& buf) { deserialize(buf); } + private: + SecureVector<byte> serialize() const; + void deserialize(const MemoryRegion<byte>&); + std::vector<X509_Certificate> certs; + }; + +/** +* Certificate Request Message +*/ +class BOTAN_DLL Certificate_Req : public HandshakeMessage + { + public: + Handshake_Type type() const { return CERTIFICATE_REQUEST; } + + std::vector<Certificate_Type> acceptable_types() const { return types; } + std::vector<X509_DN> acceptable_CAs() const { return names; } + + /* TODO + Certificate_Req(Record_Writer&, HandshakeHash&, + const X509_Certificate&); + */ + Certificate_Req(Record_Writer&, HandshakeHash&, + const std::vector<X509_Certificate>&); + + Certificate_Req(const MemoryRegion<byte>& buf) { deserialize(buf); } + private: + SecureVector<byte> serialize() const; + void deserialize(const MemoryRegion<byte>&); + + std::vector<X509_DN> names; + std::vector<Certificate_Type> types; + }; + +/** +* Certificate Verify Message +*/ +class BOTAN_DLL Certificate_Verify : public HandshakeMessage + { + public: + Handshake_Type type() const { return CERTIFICATE_VERIFY; } + + bool verify(const X509_Certificate&, HandshakeHash&); + + Certificate_Verify(RandomNumberGenerator& rng, + Record_Writer&, HandshakeHash&, + const Private_Key*); + + Certificate_Verify(const MemoryRegion<byte>& buf) { deserialize(buf); } + private: + SecureVector<byte> serialize() const; + void deserialize(const MemoryRegion<byte>&); + + SecureVector<byte> signature; + }; + +/** +* Finished Message +*/ +class BOTAN_DLL Finished : public HandshakeMessage + { + public: + Handshake_Type type() const { return FINISHED; } + + bool verify(const MemoryRegion<byte>&, Version_Code, + const HandshakeHash&, Connection_Side); + + Finished(Record_Writer&, Version_Code, Connection_Side, + const MemoryRegion<byte>&, HandshakeHash&); + Finished(const MemoryRegion<byte>& buf) { deserialize(buf); } + private: + SecureVector<byte> serialize() const; + void deserialize(const MemoryRegion<byte>&); + + SecureVector<byte> compute_verify(const MemoryRegion<byte>&, + HandshakeHash, Connection_Side, + Version_Code); + + Connection_Side side; + SecureVector<byte> verification_data; + }; + +/** +* Hello Request Message +*/ +class BOTAN_DLL Hello_Request : public HandshakeMessage + { + public: + Handshake_Type type() const { return HELLO_REQUEST; } + + Hello_Request(Record_Writer&); + Hello_Request(const MemoryRegion<byte>& buf) { deserialize(buf); } + private: + SecureVector<byte> serialize() const; + void deserialize(const MemoryRegion<byte>&); + }; + +/** +* Server Hello Message +*/ +class BOTAN_DLL Server_Hello : public HandshakeMessage + { + public: + Handshake_Type type() const { return SERVER_HELLO; } + Version_Code version() { return s_version; } + SecureVector<byte> session_id() const { return sess_id; } + u16bit ciphersuite() const { return suite; } + byte compression_algo() const { return comp_algo; } + + SecureVector<byte> random() const { return s_random; } + + Server_Hello(RandomNumberGenerator& rng, + Record_Writer&, const TLS_Policy*, + const std::vector<X509_Certificate>&, + const Client_Hello&, Version_Code, HandshakeHash&); + + Server_Hello(const MemoryRegion<byte>& buf) { deserialize(buf); } + private: + SecureVector<byte> serialize() const; + void deserialize(const MemoryRegion<byte>&); + + Version_Code s_version; + SecureVector<byte> sess_id, s_random; + u16bit suite; + byte comp_algo; + }; + +/** +* Server Key Exchange Message +*/ +class BOTAN_DLL Server_Key_Exchange : public HandshakeMessage + { + public: + Handshake_Type type() const { return SERVER_KEX; } + Public_Key* key() const; + + bool verify(const X509_Certificate&, const MemoryRegion<byte>&, + const MemoryRegion<byte>&) const; + + Server_Key_Exchange(RandomNumberGenerator& rng, + Record_Writer&, const Public_Key*, + const Private_Key*, const MemoryRegion<byte>&, + const MemoryRegion<byte>&, HandshakeHash&); + + Server_Key_Exchange(const MemoryRegion<byte>& buf) { deserialize(buf); } + private: + SecureVector<byte> serialize() const; + SecureVector<byte> serialize_params() const; + void deserialize(const MemoryRegion<byte>&); + + std::vector<BigInt> params; + SecureVector<byte> signature; + }; + +/** +* Server Hello Done Message +*/ +class BOTAN_DLL Server_Hello_Done : public HandshakeMessage + { + public: + Handshake_Type type() const { return SERVER_HELLO_DONE; } + + Server_Hello_Done(Record_Writer&, HandshakeHash&); + Server_Hello_Done(const MemoryRegion<byte>& buf) { deserialize(buf); } + private: + SecureVector<byte> serialize() const; + void deserialize(const MemoryRegion<byte>&); + }; + +} + +#endif diff --git a/src/ssl/tls_policy.cpp b/src/ssl/tls_policy.cpp new file mode 100644 index 000000000..6138ae193 --- /dev/null +++ b/src/ssl/tls_policy.cpp @@ -0,0 +1,124 @@ +/** +* Policies +* (C) 2004-2006 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/tls_policy.h> +#include <botan/tls_exceptn.h> + +namespace Botan { + +/** +* Return allowed ciphersuites +*/ +std::vector<u16bit> TLS_Policy::ciphersuites() const + { + return suite_list(allow_static_rsa(), allow_edh_rsa(), allow_edh_dsa()); + } + +/** +* Return allowed ciphersuites +*/ +std::vector<u16bit> TLS_Policy::suite_list(bool use_rsa, + bool use_edh_rsa, + bool use_edh_dsa) const + { + std::vector<u16bit> suites; + + if(use_edh_dsa) + { + suites.push_back(DHE_DSS_AES256_SHA); + suites.push_back(DHE_DSS_AES128_SHA); + suites.push_back(DHE_DSS_3DES_SHA); + } + + if(use_edh_rsa) + { + suites.push_back(DHE_RSA_AES256_SHA); + suites.push_back(DHE_RSA_AES128_SHA); + suites.push_back(DHE_RSA_3DES_SHA); + } + + if(use_rsa) + { + suites.push_back(RSA_AES256_SHA); + suites.push_back(RSA_AES128_SHA); + suites.push_back(RSA_3DES_SHA); + suites.push_back(RSA_RC4_SHA); + suites.push_back(RSA_RC4_MD5); + } + + if(suites.size() == 0) + throw TLS_Exception(INTERNAL_ERROR, + "TLS_Policy error: All ciphersuites disabled"); + + return suites; + } + +/** +* Return allowed compression algorithms +*/ +std::vector<byte> TLS_Policy::compression() const + { + std::vector<byte> algs; + algs.push_back(NO_COMPRESSION); + return algs; + } + +/** +* Choose which ciphersuite to use +*/ +u16bit TLS_Policy::choose_suite(const std::vector<u16bit>& c_suites, + bool have_rsa, + bool have_dsa) const + { + bool use_static_rsa = allow_static_rsa() && have_rsa; + bool use_edh_rsa = allow_edh_rsa() && have_rsa; + bool use_edh_dsa = allow_edh_dsa() && have_dsa; + + std::vector<u16bit> s_suites = suite_list(use_static_rsa, use_edh_rsa, + use_edh_dsa); + + for(u32bit j = 0; j != s_suites.size(); j++) + for(u32bit k = 0; k != c_suites.size(); k++) + if(s_suites[j] == c_suites[k]) + return s_suites[j]; + + return 0; + } + +/** +* Choose which compression algorithm to use +*/ +byte TLS_Policy::choose_compression(const std::vector<byte>& c_comp) const + { + std::vector<byte> s_comp = compression(); + + for(u32bit j = 0; j != s_comp.size(); j++) + for(u32bit k = 0; k != c_comp.size(); k++) + if(s_comp[j] == c_comp[k]) + return s_comp[j]; + + return NO_COMPRESSION; + } + +/** +* Return the group to use for empheral DH +*/ +DL_Group TLS_Policy::dh_group() const + { + return DL_Group("modp/ietf/1024"); + } + +/** +* Default certificate check +*/ +bool TLS_Policy::check_cert(const std::vector<X509_Certificate>&, + const std::string&) const + { + return true; + } + +} diff --git a/src/ssl/tls_policy.h b/src/ssl/tls_policy.h new file mode 100644 index 000000000..98297181c --- /dev/null +++ b/src/ssl/tls_policy.h @@ -0,0 +1,57 @@ +/** +* Policies +* (C) 2004-2006 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#ifndef BOTAN_TLS_POLICY_H__ +#define BOTAN_TLS_POLICY_H__ + +#include <botan/tls_magic.h> +#include <botan/x509cert.h> +#include <botan/dl_group.h> +#include <vector> + +namespace Botan { + +/** +* TLS_Policy Base Class +* Inherit and overload as desired to suite local policy concerns +*/ +class BOTAN_DLL TLS_Policy + { + public: + std::vector<u16bit> ciphersuites() const; + virtual std::vector<byte> compression() const; + + virtual u16bit choose_suite(const std::vector<u16bit>& client_suites, + bool rsa_ok, + bool dsa_ok) const; + + virtual byte choose_compression(const std::vector<byte>& client) const; + + virtual bool allow_static_rsa() const { return true; } + virtual bool allow_edh_rsa() const { return true; } + virtual bool allow_edh_dsa() const { return true; } + virtual bool require_client_auth() const { return false; } + + virtual DL_Group dh_group() const; + virtual u32bit rsa_export_keysize() const { return 512; } + + virtual Version_Code min_version() const { return SSL_V3; } + virtual Version_Code pref_version() const { return TLS_V10; } + + virtual bool check_cert(const std::vector<X509_Certificate>&, + const std::string&) const; + + virtual ~TLS_Policy() {} + private: + virtual std::vector<u16bit> suite_list(bool use_rsa, + bool use_edh_rsa, + bool use_edh_dsa) const; + }; + +} + +#endif diff --git a/src/ssl/tls_record.h b/src/ssl/tls_record.h new file mode 100644 index 000000000..3bec2e8ef --- /dev/null +++ b/src/ssl/tls_record.h @@ -0,0 +1,79 @@ +/** +* TLS Record Handling +* (C) 2004-2006 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#ifndef BOTAN_TLS_RECORDS_H__ +#define BOTAN_TLS_RECORDS_H__ + +#include <botan/tls_session_key.h> +#include <botan/socket.h> +#include <botan/tls_suites.h> +#include <botan/pipe.h> +#include <vector> + +namespace Botan { + +/** +* TLS Record Writer +*/ +class BOTAN_DLL Record_Writer + { + public: + void send(byte, const byte[], u32bit); + void send(byte, byte); + void flush(); + + void alert(Alert_Level, Alert_Type); + + void set_keys(const CipherSuite&, const SessionKeys&, Connection_Side); + void set_compressor(Filter*); + + void set_version(Version_Code); + + void reset(); + + Record_Writer(Socket&); + private: + void send_record(byte, const byte[], u32bit); + void send_record(byte, byte, byte, const byte[], u32bit); + + Socket& socket; + Pipe compress, cipher, mac; + SecureVector<byte> buffer; + u32bit pad_amount, mac_size, buf_pos; + u64bit seq_no; + byte major, minor, buf_type; + bool do_compress; + }; + +/** +* TLS Record Reader +*/ +class BOTAN_DLL Record_Reader + { + public: + SecureVector<byte> get_record(byte&); + + void set_keys(const CipherSuite&, const SessionKeys&, Connection_Side); + void set_compressor(Filter*); + + void set_version(Version_Code); + + void reset(); + + Record_Reader(Socket&); + private: + Socket& socket; + Pipe compress, cipher, mac; + u32bit pad_amount, mac_size; + u64bit seq_no; + byte major, minor; + bool do_compress; + }; + +} + +#endif diff --git a/src/ssl/tls_server.cpp b/src/ssl/tls_server.cpp new file mode 100644 index 000000000..37d9dbcd1 --- /dev/null +++ b/src/ssl/tls_server.cpp @@ -0,0 +1,465 @@ +/** +* TLS Server +* (C) 2004-2010 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/tls_server.h> +#include <botan/tls_alerts.h> +#include <botan/tls_state.h> +#include <botan/loadstor.h> +#include <botan/rsa.h> +#include <botan/dh.h> + +namespace Botan { + +namespace { + +/** +* Choose what version to respond with +*/ +Version_Code choose_version(Version_Code client, Version_Code minimum) + { + if(client < minimum) + throw TLS_Exception(PROTOCOL_VERSION, + "Client version is unacceptable by policy"); + + if(client == SSL_V3 || client == TLS_V10) + return client; + return TLS_V10; + } + +// FIXME: checks are wrong for session reuse (add a flag for that) +/** +* Verify the state transition is allowed +*/ +void server_check_state(Handshake_Type new_msg, Handshake_State* state) + { + class State_Transition_Error : public Unexpected_Message + { + public: + State_Transition_Error(const std::string& err) : + Unexpected_Message("State transition error from " + err) {} + }; + + if(new_msg == CLIENT_HELLO) + { + if(state->server_hello) + throw State_Transition_Error("ClientHello"); + } + else if(new_msg == CERTIFICATE) + { + if(!state->do_client_auth || !state->cert_req || + !state->server_hello_done || state->client_kex) + throw State_Transition_Error("ClientCertificate"); + } + else if(new_msg == CLIENT_KEX) + { + if(!state->server_hello_done || state->client_verify || + state->got_client_ccs) + throw State_Transition_Error("ClientKeyExchange"); + } + else if(new_msg == CERTIFICATE_VERIFY) + { + if(!state->cert_req || !state->client_certs || !state->client_kex || + state->got_client_ccs) + throw State_Transition_Error("CertificateVerify"); + } + else if(new_msg == HANDSHAKE_CCS) + { + if(!state->client_kex || state->client_finished) + throw State_Transition_Error("ClientChangeCipherSpec"); + } + else if(new_msg == FINISHED) + { + if(!state->got_client_ccs) + throw State_Transition_Error("ClientFinished"); + } + else + throw Unexpected_Message("Unexpected message in handshake"); + } + +} + +/** +* TLS Server Constructor +*/ +TLS_Server::TLS_Server(RandomNumberGenerator& r, + Socket& sock, const X509_Certificate& cert, + const Private_Key& key, const TLS_Policy* pol) : + rng(r), writer(sock), reader(sock), policy(pol ? pol : new TLS_Policy) + { + peer_id = sock.peer_id(); + + state = 0; + + cert_chain.push_back(cert); + private_key = PKCS8::copy_key(key, rng); + + try { + active = false; + writer.set_version(TLS_V10); + do_handshake(); + active = true; + } + catch(std::exception& e) + { + if(state) + { + delete state; + state = 0; + } + + writer.alert(FATAL, HANDSHAKE_FAILURE); + throw Stream_IO_Error("TLS_Server: Handshake failed"); + } + } + +/** +* TLS Server Destructor +*/ +TLS_Server::~TLS_Server() + { + close(); + delete private_key; + delete policy; + delete state; + } + +/** +* Return the peer's certificate chain +*/ +std::vector<X509_Certificate> TLS_Server::peer_cert_chain() const + { + return peer_certs; + } + +/** +* Write to a TLS connection +*/ +void TLS_Server::write(const byte buf[], u32bit length) + { + if(!active) + throw Internal_Error("TLS_Server::write called while closed"); + + writer.send(APPLICATION_DATA, buf, length); + } + +/** +* Read from a TLS connection +*/ +u32bit TLS_Server::read(byte out[], u32bit length) + { + if(!active) + throw Internal_Error("TLS_Server::read called while closed"); + + writer.flush(); + + while(read_buf.size() == 0) + { + state_machine(); + if(active == false) + break; + } + + u32bit got = std::min(read_buf.size(), length); + read_buf.read(out, got); + return got; + } + +/** +* Check connection status +*/ +bool TLS_Server::is_closed() const + { + if(!active) + return true; + return false; + } + +/** +* Close a TLS connection +*/ +void TLS_Server::close() + { + close(WARNING, CLOSE_NOTIFY); + } + +/** +* Close a TLS connection +*/ +void TLS_Server::close(Alert_Level level, Alert_Type alert_code) + { + if(active) + { + try { + active = false; + writer.alert(level, alert_code); + writer.flush(); + } + catch(...) {} + } + } + +/** +* Iterate the TLS state machine +*/ +void TLS_Server::state_machine() + { + byte rec_type; + SecureVector<byte> record = reader.get_record(rec_type); + + if(rec_type == CONNECTION_CLOSED) + { + active = false; + reader.reset(); + writer.reset(); + } + else if(rec_type == APPLICATION_DATA) + { + if(active) + read_buf.write(record, record.size()); + else + throw Unexpected_Message("Application data before handshake done"); + } + else if(rec_type == HANDSHAKE || rec_type == CHANGE_CIPHER_SPEC) + read_handshake(rec_type, record); + else if(rec_type == ALERT) + { + Alert alert(record); + + if(alert.is_fatal() || alert.type() == CLOSE_NOTIFY) + { + if(alert.type() == CLOSE_NOTIFY) + writer.alert(WARNING, CLOSE_NOTIFY); + + reader.reset(); + writer.reset(); + active = false; + } + } + else + throw Unexpected_Message("Unknown message type recieved"); + } + +/** +* Split up and process handshake messages +*/ +void TLS_Server::read_handshake(byte rec_type, + const MemoryRegion<byte>& rec_buf) + { + if(rec_type == HANDSHAKE) + state->queue.write(rec_buf, rec_buf.size()); + + while(true) + { + Handshake_Type type = HANDSHAKE_NONE; + SecureVector<byte> contents; + + if(rec_type == HANDSHAKE) + { + if(state->queue.size() >= 4) + { + byte head[4] = { 0 }; + state->queue.peek(head, 4); + + const u32bit length = make_u32bit(0, head[1], head[2], head[3]); + + if(state->queue.size() >= length + 4) + { + type = static_cast<Handshake_Type>(head[0]); + contents.resize(length); + state->queue.read(head, 4); + state->queue.read(contents, contents.size()); + } + } + } + else if(rec_type == CHANGE_CIPHER_SPEC) + { + if(state->queue.size() == 0 && rec_buf.size() == 1 && rec_buf[0] == 1) + type = HANDSHAKE_CCS; + else + throw Decoding_Error("Malformed ChangeCipherSpec message"); + } + else + throw Decoding_Error("Unknown message type in handshake processing"); + + if(type == HANDSHAKE_NONE) + break; + + process_handshake_msg(type, contents); + + if(type == HANDSHAKE_CCS || !state) + break; + } + } + +/** +* Process a handshake message +*/ +void TLS_Server::process_handshake_msg(Handshake_Type type, + const MemoryRegion<byte>& contents) + { + if(type == CLIENT_HELLO) + { + if(state == 0) + state = new Handshake_State(); + else + return; + } + + if(state == 0) + throw Unexpected_Message("Unexpected handshake message"); + + if(type != HANDSHAKE_CCS && type != FINISHED) + { + state->hash.update(static_cast<byte>(type)); + u32bit record_length = contents.size(); + for(u32bit j = 0; j != 3; j++) + state->hash.update(get_byte(j+1, record_length)); + state->hash.update(contents); + } + + if(type == CLIENT_HELLO) + { + server_check_state(type, state); + + state->client_hello = new Client_Hello(contents); + + state->version = choose_version(state->client_hello->version(), + policy->min_version()); + + writer.set_version(state->version); + reader.set_version(state->version); + + state->server_hello = new Server_Hello(rng, writer, + policy, cert_chain, + *(state->client_hello), + state->version, state->hash); + + state->suite = CipherSuite(state->server_hello->ciphersuite()); + + if(state->suite.sig_type() != CipherSuite::NO_SIG) + { + // FIXME: should choose certs based on sig type + state->server_certs = new Certificate(writer, cert_chain, + state->hash); + } + + state->kex_priv = PKCS8::copy_key(*private_key, rng); + if(state->suite.kex_type() != CipherSuite::NO_KEX) + { + if(state->suite.kex_type() == CipherSuite::RSA_KEX) + { + state->kex_priv = new RSA_PrivateKey(rng, + policy->rsa_export_keysize()); + } + else if(state->suite.kex_type() == CipherSuite::DH_KEX) + { + state->kex_priv = new DH_PrivateKey(rng, policy->dh_group()); + } + else + throw Internal_Error("TLS_Server: Unknown ciphersuite kex type"); + + state->server_kex = + new Server_Key_Exchange(rng, writer, + state->kex_priv, private_key, + state->client_hello->random(), + state->server_hello->random(), + state->hash); + } + + if(policy->require_client_auth()) + { + state->do_client_auth = true; + throw Internal_Error("Client auth not implemented"); + // FIXME: send client auth request here + } + + state->server_hello_done = new Server_Hello_Done(writer, state->hash); + } + else if(type == CERTIFICATE) + { + server_check_state(type, state); + // FIXME: process this + } + else if(type == CLIENT_KEX) + { + server_check_state(type, state); + + state->client_kex = new Client_Key_Exchange(contents, state->suite, + state->version); + + SecureVector<byte> pre_master = + state->client_kex->pre_master_secret(rng, state->kex_priv, + state->server_hello->version()); + + state->keys = SessionKeys(state->suite, state->version, pre_master, + state->client_hello->random(), + state->server_hello->random()); + } + else if(type == CERTIFICATE_VERIFY) + { + server_check_state(type, state); + // FIXME: process this + } + else if(type == HANDSHAKE_CCS) + { + server_check_state(type, state); + + reader.set_keys(state->suite, state->keys, SERVER); + state->got_client_ccs = true; + } + else if(type == FINISHED) + { + server_check_state(type, state); + + state->client_finished = new Finished(contents); + + if(!state->client_finished->verify(state->keys.master_secret(), + state->version, state->hash, CLIENT)) + throw TLS_Exception(DECRYPT_ERROR, + "Finished message didn't verify"); + + state->hash.update(static_cast<byte>(type)); + u32bit record_length = contents.size(); + for(u32bit j = 0; j != 3; j++) + state->hash.update(get_byte(j+1, record_length)); + state->hash.update(contents); + + writer.send(CHANGE_CIPHER_SPEC, 1); + writer.flush(); + + writer.set_keys(state->suite, state->keys, SERVER); + + state->server_finished = new Finished(writer, state->version, SERVER, + state->keys.master_secret(), + state->hash); + + delete state; + state = 0; + active = true; + } + else + throw Unexpected_Message("Unknown handshake message recieved"); + } + +/** +* Perform a server-side TLS handshake +*/ +void TLS_Server::do_handshake() + { + while(true) + { + if(active && !state) + break; + + state_machine(); + + if(!active && !state) + throw TLS_Exception(HANDSHAKE_FAILURE, "TLS_Server: Handshake failed"); + } + } + +} diff --git a/src/ssl/tls_server.h b/src/ssl/tls_server.h new file mode 100644 index 000000000..683c3413a --- /dev/null +++ b/src/ssl/tls_server.h @@ -0,0 +1,69 @@ +/** +* TLS Server +* (C) 2004-2010 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#ifndef BOTAN_TLS_SERVER_H__ +#define BOTAN_TLS_SERVER_H__ + +#include <botan/tls_connection.h> +#include <botan/tls_state.h> +#include <vector> + +namespace Botan { + +/** +* TLS Server +*/ + +class BOTAN_DLL TLS_Server : public TLS_Connection + { + public: + u32bit read(byte buf[], u32bit buf_len); + void write(const byte buf[], u32bit buf_len); + + std::vector<X509_Certificate> peer_cert_chain() const; + + void close(); + bool is_closed() const; + + // FIXME: support cert chains (!) + // FIXME: support anonymous servers + TLS_Server(RandomNumberGenerator& rng, + Socket& peer, + const X509_Certificate& cert, + const Private_Key& cert_key, + const TLS_Policy* policy = 0); + + ~TLS_Server(); + private: + void close(Alert_Level, Alert_Type); + + void do_handshake(); + void state_machine(); + void read_handshake(byte, const MemoryRegion<byte>&); + + void process_handshake_msg(Handshake_Type, const MemoryRegion<byte>&); + + RandomNumberGenerator& rng; + + Record_Writer writer; + Record_Reader reader; + const TLS_Policy* policy; + + // FIXME: rename to match TLS_Client + std::vector<X509_Certificate> cert_chain, peer_certs; + Private_Key* private_key; + + Handshake_State* state; + SecureVector<byte> session_id; + SecureQueue read_buf; + std::string peer_id; + bool active; + }; + +} + +#endif diff --git a/src/ssl/tls_session_key.cpp b/src/ssl/tls_session_key.cpp new file mode 100644 index 000000000..83c06ba07 --- /dev/null +++ b/src/ssl/tls_session_key.cpp @@ -0,0 +1,170 @@ +/** +* TLS Session Key +* (C) 2004-2006 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/tls_session_key.h> +#include <botan/prf_ssl3.h> +#include <botan/prf_tls.h> +#include <botan/lookup.h> + +namespace Botan { + +/** +* Return the client cipher key +*/ +SymmetricKey SessionKeys::client_cipher_key() const + { + return c_cipher; + } + +/** +* Return the server cipher key +*/ +SymmetricKey SessionKeys::server_cipher_key() const + { + return s_cipher; + } + +/** +* Return the client MAC key +*/ +SymmetricKey SessionKeys::client_mac_key() const + { + return c_mac; + } + +/** +* Return the server MAC key +*/ +SymmetricKey SessionKeys::server_mac_key() const + { + return s_mac; + } + +/** +* Return the client cipher IV +*/ +InitializationVector SessionKeys::client_iv() const + { + return c_iv; + } + +/** +* Return the server cipher IV +*/ +InitializationVector SessionKeys::server_iv() const + { + return s_iv; + } + +/** +* Return the TLS master secret +*/ +SecureVector<byte> SessionKeys::master_secret() const + { + return master_sec; + } + +/** +* Generate SSLv3 session keys +*/ +SymmetricKey SessionKeys::ssl3_keygen(u32bit prf_gen, + const MemoryRegion<byte>& pre_master, + const MemoryRegion<byte>& client_random, + const MemoryRegion<byte>& server_random) + { + SSL3_PRF prf; + + SecureVector<byte> salt; + salt.append(client_random); + salt.append(server_random); + + master_sec = prf.derive_key(48, pre_master, salt); + + salt.destroy(); + salt.append(server_random); + salt.append(client_random); + + return prf.derive_key(prf_gen, master_sec, salt); + } + +/** +* Generate TLS 1.0 session keys +*/ +SymmetricKey SessionKeys::tls1_keygen(u32bit prf_gen, + const MemoryRegion<byte>& pre_master, + const MemoryRegion<byte>& client_random, + const MemoryRegion<byte>& server_random) + { + const byte MASTER_SECRET_MAGIC[] = { + 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x73, 0x65, 0x63, 0x72, 0x65, + 0x74 }; + const byte KEY_GEN_MAGIC[] = { + 0x6B, 0x65, 0x79, 0x20, 0x65, 0x78, 0x70, 0x61, 0x6E, 0x73, 0x69, 0x6F, + 0x6E }; + + TLS_PRF prf; + + SecureVector<byte> salt; + salt.append(MASTER_SECRET_MAGIC, sizeof(MASTER_SECRET_MAGIC)); + salt.append(client_random); + salt.append(server_random); + + master_sec = prf.derive_key(48, pre_master, salt); + + salt.destroy(); + salt.append(KEY_GEN_MAGIC, sizeof(KEY_GEN_MAGIC)); + salt.append(server_random); + salt.append(client_random); + + return prf.derive_key(prf_gen, master_sec, salt); + } + +/** +* SessionKeys Constructor +*/ +SessionKeys::SessionKeys(const CipherSuite& suite, Version_Code version, + const MemoryRegion<byte>& pre_master_secret, + const MemoryRegion<byte>& c_random, + const MemoryRegion<byte>& s_random) + { + if(version != SSL_V3 && version != TLS_V10) + throw Invalid_Argument("SessionKeys: Unknown version code"); + + const u32bit mac_keylen = output_length_of(suite.mac_algo()); + u32bit cipher_keylen = suite.cipher_keylen(); + + u32bit cipher_ivlen = 0; + if(have_block_cipher(suite.cipher_algo())) + cipher_ivlen = block_size_of(suite.cipher_algo()); + + const u32bit prf_gen = 2 * (mac_keylen + cipher_keylen + cipher_ivlen); + + SymmetricKey keyblock = (version == SSL_V3) ? + ssl3_keygen(prf_gen, pre_master_secret, c_random, s_random) : + tls1_keygen(prf_gen, pre_master_secret, c_random, s_random); + + const byte* key_data = keyblock.begin(); + + c_mac = SymmetricKey(key_data, mac_keylen); + key_data += mac_keylen; + + s_mac = SymmetricKey(key_data, mac_keylen); + key_data += mac_keylen; + + c_cipher = SymmetricKey(key_data, cipher_keylen); + key_data += cipher_keylen; + + s_cipher = SymmetricKey(key_data, cipher_keylen); + key_data += cipher_keylen; + + c_iv = InitializationVector(key_data, cipher_ivlen); + key_data += cipher_ivlen; + + s_iv = InitializationVector(key_data, cipher_ivlen); + } + +} diff --git a/src/ssl/tls_session_key.h b/src/ssl/tls_session_key.h new file mode 100644 index 000000000..b0eba2eb1 --- /dev/null +++ b/src/ssl/tls_session_key.h @@ -0,0 +1,52 @@ +/** +* TLS Session Key +* (C) 2004-2006 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#ifndef BOTAN_TLS_SESSION_KEYS_H__ +#define BOTAN_TLS_SESSION_KEYS_H__ + +#include <botan/tls_suites.h> +#include <botan/tls_exceptn.h> +#include <botan/symkey.h> + +namespace Botan { + +/** +* TLS Session Keys +*/ +class BOTAN_DLL SessionKeys + { + public: + SymmetricKey client_cipher_key() const; + SymmetricKey server_cipher_key() const; + + SymmetricKey client_mac_key() const; + SymmetricKey server_mac_key() const; + + InitializationVector client_iv() const; + InitializationVector server_iv() const; + + SecureVector<byte> master_secret() const; + + SessionKeys() {} + SessionKeys(const CipherSuite&, Version_Code, const MemoryRegion<byte>&, + const MemoryRegion<byte>&, const MemoryRegion<byte>&); + private: + SymmetricKey ssl3_keygen(u32bit, const MemoryRegion<byte>&, + const MemoryRegion<byte>&, + const MemoryRegion<byte>&); + SymmetricKey tls1_keygen(u32bit, const MemoryRegion<byte>&, + const MemoryRegion<byte>&, + const MemoryRegion<byte>&); + + SecureVector<byte> master_sec; + SymmetricKey c_cipher, s_cipher, c_mac, s_mac; + InitializationVector c_iv, s_iv; + }; + +} + +#endif diff --git a/src/ssl/tls_state.h b/src/ssl/tls_state.h new file mode 100644 index 000000000..ddf03a822 --- /dev/null +++ b/src/ssl/tls_state.h @@ -0,0 +1,53 @@ +/** +* TLS Handshaking +* (C) 2004-2006 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#ifndef BOTAN_TLS_HANDSHAKE_H__ +#define BOTAN_TLS_HANDSHAKE_H__ + +#include <botan/tls_messages.h> +#include <botan/secqueue.h> + +namespace Botan { + +/** +* SSL/TLS Handshake State +*/ +class BOTAN_DLL Handshake_State + { + public: + Client_Hello* client_hello; + Server_Hello* server_hello; + Certificate* server_certs; + Server_Key_Exchange* server_kex; + Certificate_Req* cert_req; + Server_Hello_Done* server_hello_done; + + Certificate* client_certs; + Client_Key_Exchange* client_kex; + Certificate_Verify* client_verify; + Finished* client_finished; + Finished* server_finished; + + Public_Key* kex_pub; + Private_Key* kex_priv; + + CipherSuite suite; + SessionKeys keys; + HandshakeHash hash; + + SecureQueue queue; + + Version_Code version; + bool got_client_ccs, got_server_ccs, do_client_auth; + + Handshake_State(); + ~Handshake_State(); + }; + +} + +#endif diff --git a/src/ssl/tls_suites.cpp b/src/ssl/tls_suites.cpp new file mode 100644 index 000000000..26fa75428 --- /dev/null +++ b/src/ssl/tls_suites.cpp @@ -0,0 +1,77 @@ +/** +* TLS Cipher Suites +* (C) 2004-2006 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/tls_suites.h> +#include <botan/tls_exceptn.h> +#include <botan/parsing.h> +#include <vector> +#include <string> + +namespace Botan { + +namespace { + +/** +* Convert an SSL/TLS ciphersuite to a string +*/ +std::string lookup_ciphersuite(u16bit suite) + { + if(suite == RSA_RC4_MD5) return "RSA/NONE/ARC4/16/MD5"; + if(suite == RSA_RC4_SHA) return "RSA/NONE/ARC4/16/SHA1"; + if(suite == RSA_3DES_SHA) return "RSA/NONE/3DES/24/SHA1"; + if(suite == RSA_AES128_SHA) return "RSA/NONE/AES/16/SHA1"; + if(suite == RSA_AES256_SHA) return "RSA/NONE/AES/32/SHA1"; + + if(suite == DHE_RSA_3DES_SHA) return "RSA/DH/3DES/24/SHA1"; + if(suite == DHE_RSA_AES128_SHA) return "RSA/DH/AES/16/SHA1"; + if(suite == DHE_RSA_AES256_SHA) return "RSA/DH/AES/32/SHA1"; + + if(suite == DHE_DSS_3DES_SHA) return "DSA/DH/3DES/24/SHA1"; + if(suite == DHE_DSS_AES128_SHA) return "DSA/DH/AES/16/SHA1"; + if(suite == DHE_DSS_AES256_SHA) return "DSA/DH/AES/32/SHA1"; + + return ""; + } + +} + +/** +* CipherSuite Constructor +*/ +CipherSuite::CipherSuite(u16bit suite_code) + { + if(suite_code == 0) + return; + + std::string suite_string = lookup_ciphersuite(suite_code); + + if(suite_string == "") + throw Invalid_Argument("Unknown ciphersuite: " + + to_string(suite_code)); + + std::vector<std::string> suite_info = split_on(suite_string, '/'); + + if(suite_info[0] == "RSA") sig_algo = RSA_SIG; + else if(suite_info[0] == "DSA") sig_algo = DSA_SIG; + else if(suite_info[0] == "NONE") sig_algo = NO_SIG; + else + throw TLS_Exception(INTERNAL_ERROR, + "CipherSuite: Unknown sig type " + suite_info[0]); + + if(suite_info[1] == "DH") kex_algo = DH_KEX; + else if(suite_info[1] == "RSA") kex_algo = RSA_KEX; + else if(suite_info[1] == "NONE") kex_algo = NO_KEX; + else + throw TLS_Exception(INTERNAL_ERROR, + "CipherSuite: Unknown kex type " + suite_info[1]); + + cipher = suite_info[2]; + cipher_key_length = to_u32bit(suite_info[3]); + mac = suite_info[4]; + } + +} diff --git a/src/ssl/tls_suites.h b/src/ssl/tls_suites.h new file mode 100644 index 000000000..b7008e8db --- /dev/null +++ b/src/ssl/tls_suites.h @@ -0,0 +1,42 @@ +/** +* Cipher Suites +* (C) 2004-2006 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#ifndef BOTAN_TLS_CIPHERSUITES_H__ +#define BOTAN_TLS_CIPHERSUITES_H__ + +#include <botan/types.h> +#include <string> + +namespace Botan { + +/** +* Ciphersuite Information +*/ +class BOTAN_DLL CipherSuite + { + public: + enum Kex_Type { NO_KEX, RSA_KEX, DH_KEX }; + enum Sig_Type { NO_SIG, RSA_SIG, DSA_SIG }; + + std::string cipher_algo() const { return cipher; } + std::string mac_algo() const { return mac; } + + u32bit cipher_keylen() const { return cipher_key_length; } + Kex_Type kex_type() const { return kex_algo; } + Sig_Type sig_type() const { return sig_algo; } + + CipherSuite(u16bit = 0); + private: + Kex_Type kex_algo; + Sig_Type sig_algo; + std::string cipher, mac; + u32bit cipher_key_length; + }; + +} + +#endif diff --git a/src/ssl/unix_socket/info.txt b/src/ssl/unix_socket/info.txt new file mode 100644 index 000000000..205d0c700 --- /dev/null +++ b/src/ssl/unix_socket/info.txt @@ -0,0 +1,21 @@ +define UNIX_SOCKET + +<source> +unx_sock.cpp +</source> + +<header:public> +unx_sock.h +</header:public> + +<requires> +ssl +</requires> + +<os> +linux +freebsd +netbsd +openbsd +solaris +</os> diff --git a/src/ssl/unix_socket/unx_sock.cpp b/src/ssl/unix_socket/unx_sock.cpp new file mode 100644 index 000000000..ca4d476b7 --- /dev/null +++ b/src/ssl/unix_socket/unx_sock.cpp @@ -0,0 +1,200 @@ +/** +* Unix Socket +* (C) 2004-2006 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/unx_sock.h> +#include <botan/exceptn.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <netdb.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> + +namespace Botan { + +/** +* Unix Socket Constructor +*/ +Unix_Socket::Unix_Socket(const std::string& host, u16bit port) : peer(host) + { + sockfd = -1; + + hostent* host_addr = ::gethostbyname(host.c_str()); + + if(host_addr == 0) + throw Stream_IO_Error("Unix_Socket: gethostbyname failed for " + host); + if(host_addr->h_addrtype != AF_INET) // FIXME + throw Stream_IO_Error("Unix_Socket: " + host + " has IPv6 address"); + + int fd = ::socket(PF_INET, SOCK_STREAM, 0); + if(fd == -1) + throw Stream_IO_Error("Unix_Socket: Unable to acquire socket"); + + sockaddr_in socket_info; + ::memset(&socket_info, 0, sizeof(socket_info)); + socket_info.sin_family = AF_INET; + socket_info.sin_port = htons(port); + socket_info.sin_addr = *(struct in_addr*)host_addr->h_addr; // FIXME + + if(::connect(fd, (sockaddr*)&socket_info, sizeof(struct sockaddr)) != 0) + { + ::close(fd); + throw Stream_IO_Error("Unix_Socket: connect failed"); + } + + sockfd = fd; + } + +/** +* Unix Socket Constructor +*/ +Unix_Socket::Unix_Socket(int fd, const std::string& peer_id) + { + sockfd = fd; + peer = peer_id; + } + +/** +* Read from a Unix socket +*/ +u32bit Unix_Socket::read(byte buf[], u32bit length) + { + if(sockfd == -1) + throw Stream_IO_Error("Unix_Socket::read: Socket not connected"); + + u32bit got = 0; + + while(length) + { + ssize_t this_time = ::recv(sockfd, buf + got, length, MSG_NOSIGNAL); + + if(this_time == 0) + break; + + if(this_time == -1) + { + if(errno == EINTR) + this_time = 0; + else + throw Stream_IO_Error("Unix_Socket::read: Socket read failed"); + } + + got += this_time; + length -= this_time; + } + return got; + } + +/** +* Write to a Unix socket +*/ +void Unix_Socket::write(const byte buf[], u32bit length) + { + if(sockfd == -1) + throw Stream_IO_Error("Unix_Socket::write: Socket not connected"); + + u32bit offset = 0; + while(length) + { + ssize_t sent = ::send(sockfd, buf + offset, length, MSG_NOSIGNAL); + + if(sent == -1) + { + if(errno == EINTR) + sent = 0; + else + throw Stream_IO_Error("Unix_Socket::write: Socket write failed"); + } + + offset += sent; + length -= sent; + } + } + +/** +* Close a Unix socket +*/ +void Unix_Socket::close() + { + if(sockfd != -1) + { + if(::close(sockfd) != 0) + throw Stream_IO_Error("Unix_Socket::close failed"); + sockfd = -1; + } + } + +/** +* Return the peer's name +*/ +std::string Unix_Socket::peer_id() const + { + return peer; + } + +/** +* Unix Server Socket Constructor +*/ +Unix_Server_Socket::Unix_Server_Socket(u16bit port) + { + sockfd = -1; + + int fd = ::socket(PF_INET, SOCK_STREAM, 0); + if(fd == -1) + throw Stream_IO_Error("Unix_Server_Socket: Unable to acquire socket"); + + sockaddr_in socket_info; + ::memset(&socket_info, 0, sizeof(socket_info)); + socket_info.sin_family = AF_INET; + socket_info.sin_port = htons(port); + + // FIXME: support limiting listeners + socket_info.sin_addr.s_addr = INADDR_ANY; + + if(::bind(fd, (sockaddr*)&socket_info, sizeof(struct sockaddr)) != 0) + { + ::close(fd); + throw Stream_IO_Error("Unix_Server_Socket: bind failed"); + } + + if(listen(fd, 100) != 0) // FIXME: totally arbitrary + { + ::close(fd); + throw Stream_IO_Error("Unix_Server_Socket: listen failed"); + } + + sockfd = fd; + } + +/** +* Close a Unix socket +*/ +void Unix_Server_Socket::close() + { + if(sockfd != -1) + { + if(::close(sockfd) != 0) + throw Stream_IO_Error("Unix_Server_Socket::close failed"); + sockfd = -1; + } + } + +/** +* Accept a new connection +*/ +Socket* Unix_Server_Socket::accept() + { + // FIXME: grab IP of remote side, use gethostbyaddr, store as peer_id + int retval = ::accept(sockfd, 0, 0); + if(retval == -1) + throw Stream_IO_Error("Unix_Server_Socket: accept failed"); + return new Unix_Socket(retval); + } + +} diff --git a/src/ssl/unix_socket/unx_sock.h b/src/ssl/unix_socket/unx_sock.h new file mode 100644 index 000000000..c1ff53ae3 --- /dev/null +++ b/src/ssl/unix_socket/unx_sock.h @@ -0,0 +1,62 @@ +/* +* Unix Socket +* (C) 2004-2006 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#ifndef BOTAN_TLS_SOCKET_UNIX_H__ +#define BOTAN_TLS_SOCKET_UNIX_H__ + +#include <botan/socket.h> + +namespace Botan { + +/** + FIXME: the current socket interface is totally unusable + It has to handle (cleanly): + - TCP, UDP, and SCTP, where UDP is only usable with DTLS and + TCP/SCTP is only usable with TLS. + - Alternate socket interfaces (ACE, Netxx, whatever) with + minimal wrapping needed. +*/ + + +/** +* Unix Socket Base Class +*/ +class BOTAN_DLL Unix_Socket : public Socket + { + public: + u32bit read(byte[], u32bit); + void write(const byte[], u32bit); + + std::string peer_id() const; + + void close(); + Unix_Socket(int, const std::string& = ""); + Unix_Socket(const std::string&, u16bit); + ~Unix_Socket() { close(); } + private: + std::string peer; + int sockfd; + }; + +/** +* Unix Server Socket Base Class +*/ +class BOTAN_DLL Unix_Server_Socket : public Server_Socket + { + public: + Socket* accept(); + void close(); + + Unix_Server_Socket(u16bit); + ~Unix_Server_Socket() { close(); } + private: + int sockfd; + }; + +} + +#endif |