/* * Copyright (C) 2016 Charybdis Development Team * Copyright (C) 2016 Jason Volk * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice is present in all copies. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include // // request // ircd::string_view ircd::m::request::operator()(const mutable_buffer &out, const vector_view &addl_headers) const { const size_t addl_headers_size { std::min(addl_headers.size(), size_t(64UL)) }; size_t headers{1}; http::header header[headers + addl_headers_size] { { "User-Agent", BRANDING_NAME " (IRCd " BRANDING_VERSION ")" }, }; for(size_t i(0); i < addl_headers_size; ++i) header[headers++] = addl_headers.at(i); thread_local char x_matrix[1_KiB]; if(startswith(at<"uri"_>(*this), "/_matrix/federation")) { const auto &sk{self::secret_key}; const auto &pkid{self::public_key_id}; header[headers++] = { "Authorization", generate(x_matrix, sk, pkid) }; } static const string_view content_type { "application/json; charset=utf-8"_sv }; const auto content_length { string_view(json::get<"content"_>(*this)).size() }; stream_buffer sb{out}; http::request { sb, at<"destination"_>(*this), at<"method"_>(*this), at<"uri"_>(*this), content_length, content_type, { header, headers } }; return sb.completed(); } ircd::string_view ircd::m::request::generate(const mutable_buffer &out, const ed25519::sk &sk, const string_view &pkid) const { const json::strung object { *this, }; const ed25519::sig sig { self::secret_key.sign(const_buffer{object}) }; const auto &origin { unquote(string_view{at<"origin"_>(*this)}) }; thread_local char sigb64[1_KiB]; return fmt::sprintf { out, "X-Matrix origin=%s,key=\"%s\",sig=\"%s\"", origin, pkid, b64encode_unpadded(sigb64, sig) }; } bool ircd::m::request::verify(const string_view &key, const string_view &sig) const { const ed25519::sig _sig { [&sig](auto &buf) { b64decode(buf, sig); } }; const auto &origin { unquote(at<"origin"_>(*this)) }; const ed25519::pk pk { [&origin, &key](auto &buf) { m::keys::get(origin, key, [&buf] (const string_view &key) { b64decode(buf, unquote(key)); }); } }; return verify(pk, _sig); } bool ircd::m::request::verify(const ed25519::pk &pk, const ed25519::sig &sig) const { const json::strung object { *this }; return verify(pk, sig, object); } bool ircd::m::request::verify(const ed25519::pk &pk, const ed25519::sig &sig, const json::object &object) { return pk.verify(object, sig); } // // x_matrix // ircd::m::request::x_matrix::x_matrix(const string_view &input) { string_view tokens[3]; if(ircd::tokens(split(input, ' ').second, ',', tokens) != 3) throw std::out_of_range{"The x_matrix header is malformed"}; for(const auto &token : tokens) { const auto &kv{split(token, '=')}; const auto &val{unquote(kv.second)}; switch(hash(kv.first)) { case hash("origin"): origin = val; break; case hash("key"): key = val; break; case hash("sig"): sig = val; break; } } if(empty(origin)) throw std::out_of_range{"The x_matrix header is missing 'origin='"}; if(empty(key)) throw std::out_of_range{"The x_matrix header is missing 'key='"}; if(empty(sig)) throw std::out_of_range{"The x_matrix header is missing 'sig='"}; }