diff options
Diffstat (limited to 'src/tls/cert_req.cpp')
-rw-r--r-- | src/tls/cert_req.cpp | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/src/tls/cert_req.cpp b/src/tls/cert_req.cpp new file mode 100644 index 000000000..df70cc43d --- /dev/null +++ b/src/tls/cert_req.cpp @@ -0,0 +1,239 @@ +/* +* Certificate Request Message +* (C) 2004-2006,2012 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/internal/tls_messages.h> +#include <botan/internal/tls_reader.h> +#include <botan/internal/tls_extensions.h> +#include <botan/tls_record.h> +#include <botan/der_enc.h> +#include <botan/ber_dec.h> +#include <botan/loadstor.h> + +namespace Botan { + +namespace TLS { + +namespace { + +std::string cert_type_code_to_name(byte code) + { + switch(code) + { + case 1: + return "RSA"; + case 2: + return "DSA"; + case 64: + return "ECDSA"; + default: + return ""; // DH or something else + } + } + +byte cert_type_name_to_code(const std::string& name) + { + if(name == "RSA") + return 1; + if(name == "DSA") + return 2; + if(name == "ECDSA") + return 64; + + throw Invalid_Argument("Unknown cert type " + name); + } + +} + +/** +* Create a new Certificate Request message +*/ +Certificate_Req::Certificate_Req(Record_Writer& writer, + Handshake_Hash& hash, + const Policy& policy, + const std::vector<X509_Certificate>& ca_certs, + Protocol_Version version) + { + for(size_t i = 0; i != ca_certs.size(); ++i) + names.push_back(ca_certs[i].subject_dn()); + + cert_key_types.push_back("RSA"); + cert_key_types.push_back("DSA"); + cert_key_types.push_back("ECDSA"); + + if(version >= Protocol_Version::TLS_V12) + { + std::vector<std::string> hashes = policy.allowed_hashes(); + std::vector<std::string> sigs = policy.allowed_signature_methods(); + + for(size_t i = 0; i != hashes.size(); ++i) + for(size_t j = 0; j != sigs.size(); ++j) + m_supported_algos.push_back(std::make_pair(hashes[i], sigs[j])); + } + + hash.update(writer.send(*this)); + } + +/** +* Deserialize a Certificate Request message +*/ +Certificate_Req::Certificate_Req(const MemoryRegion<byte>& buf, + Protocol_Version version) + { + if(buf.size() < 4) + throw Decoding_Error("Certificate_Req: Bad certificate request"); + + TLS_Data_Reader reader(buf); + + std::vector<byte> cert_type_codes = reader.get_range_vector<byte>(1, 1, 255); + + for(size_t i = 0; i != cert_type_codes.size(); ++i) + { + const std::string cert_type_name = cert_type_code_to_name(cert_type_codes[i]); + + if(cert_type_name == "") // something we don't know + continue; + + cert_key_types.push_back(cert_type_name); + } + + if(version >= Protocol_Version::TLS_V12) + { + std::vector<byte> sig_hash_algs = reader.get_range_vector<byte>(2, 2, 65534); + + if(sig_hash_algs.size() % 2 != 0) + throw Decoding_Error("Bad length for signature IDs in certificate request"); + + for(size_t i = 0; i != sig_hash_algs.size(); i += 2) + { + std::string hash = Signature_Algorithms::hash_algo_name(sig_hash_algs[i]); + std::string sig = Signature_Algorithms::sig_algo_name(sig_hash_algs[i+1]); + m_supported_algos.push_back(std::make_pair(hash, sig)); + } + } + else + { + // The hardcoded settings from previous protocol versions + m_supported_algos.push_back(std::make_pair("TLS.Digest.0", "RSA")); + m_supported_algos.push_back(std::make_pair("SHA-1", "DSA")); + m_supported_algos.push_back(std::make_pair("SHA-1", "ECDSA")); + } + + u16bit purported_size = reader.get_u16bit(); + + if(reader.remaining_bytes() != purported_size) + throw Decoding_Error("Inconsistent length in certificate request"); + + while(reader.has_remaining()) + { + std::vector<byte> name_bits = reader.get_range_vector<byte>(2, 0, 65535); + + BER_Decoder decoder(&name_bits[0], name_bits.size()); + X509_DN name; + decoder.decode(name); + names.push_back(name); + } + } + +/** +* Serialize a Certificate Request message +*/ +MemoryVector<byte> Certificate_Req::serialize() const + { + MemoryVector<byte> buf; + + std::vector<byte> cert_types; + + for(size_t i = 0; i != cert_key_types.size(); ++i) + cert_types.push_back(cert_type_name_to_code(cert_key_types[i])); + + append_tls_length_value(buf, cert_types, 1); + + if(!m_supported_algos.empty()) + { + buf += Signature_Algorithms(m_supported_algos).serialize(); + } + + for(size_t i = 0; i != names.size(); ++i) + { + DER_Encoder encoder; + encoder.encode(names[i]); + + append_tls_length_value(buf, encoder.get_contents(), 2); + } + + return buf; + } + +/** +* Create a new Certificate message +*/ +Certificate::Certificate(Record_Writer& writer, + Handshake_Hash& hash, + const std::vector<X509_Certificate>& cert_list) : + m_certs(cert_list) + { + hash.update(writer.send(*this)); + } + +/** +* Deserialize a Certificate message +*/ +Certificate::Certificate(const MemoryRegion<byte>& buf) + { + if(buf.size() < 3) + throw Decoding_Error("Certificate: Message malformed"); + + const size_t total_size = make_u32bit(0, buf[0], buf[1], buf[2]); + + if(total_size != buf.size() - 3) + throw Decoding_Error("Certificate: Message malformed"); + + const byte* certs = &buf[3]; + + while(certs != buf.end()) + { + if(buf.end() - certs < 3) + throw Decoding_Error("Certificate: Message malformed"); + + const size_t cert_size = make_u32bit(0, certs[0], certs[1], certs[2]); + + if(buf.end() - certs < (3 + cert_size)) + throw Decoding_Error("Certificate: Message malformed"); + + DataSource_Memory cert_buf(&certs[3], cert_size); + m_certs.push_back(X509_Certificate(cert_buf)); + + certs += cert_size + 3; + } + } + +/** +* Serialize a Certificate message +*/ +MemoryVector<byte> Certificate::serialize() const + { + MemoryVector<byte> buf(3); + + for(size_t i = 0; i != m_certs.size(); ++i) + { + MemoryVector<byte> raw_cert = m_certs[i].BER_encode(); + const size_t cert_size = raw_cert.size(); + for(size_t i = 0; i != 3; ++i) + buf.push_back(get_byte<u32bit>(i+1, cert_size)); + buf += raw_cert; + } + + const size_t buf_size = buf.size() - 3; + for(size_t i = 0; i != 3; ++i) + buf[i] = get_byte<u32bit>(i+1, buf_size); + + return buf; + } + +} + +} |