ircd:Ⓜ️:fetch: Split fetch_check related into unit.
This commit is contained in:
parent
c32928981b
commit
ed5dfd0031
|
@ -163,6 +163,7 @@ libircd_matrix_la_SOURCES += fed.cc
|
||||||
libircd_matrix_la_SOURCES += fed_well_known.cc
|
libircd_matrix_la_SOURCES += fed_well_known.cc
|
||||||
libircd_matrix_la_SOURCES += feds.cc
|
libircd_matrix_la_SOURCES += feds.cc
|
||||||
libircd_matrix_la_SOURCES += fetch.cc
|
libircd_matrix_la_SOURCES += fetch.cc
|
||||||
|
libircd_matrix_la_SOURCES += fetch_check.cc
|
||||||
libircd_matrix_la_SOURCES += gossip.cc
|
libircd_matrix_la_SOURCES += gossip.cc
|
||||||
libircd_matrix_la_SOURCES += groups.cc
|
libircd_matrix_la_SOURCES += groups.cc
|
||||||
libircd_matrix_la_SOURCES += request.cc
|
libircd_matrix_la_SOURCES += request.cc
|
||||||
|
|
278
matrix/fetch.cc
278
matrix/fetch.cc
|
@ -30,8 +30,7 @@ namespace ircd::m::fetch
|
||||||
extern log::log log;
|
extern log::log log;
|
||||||
|
|
||||||
static bool timedout(const request &, const system_point &now);
|
static bool timedout(const request &, const system_point &now);
|
||||||
static void _check_event(const request &, const m::event &);
|
extern void check_response(const request &, const json::object &);
|
||||||
static void check_response(const request &, const json::object &);
|
|
||||||
static bool proffer_remote(request &, const string_view &);
|
static bool proffer_remote(request &, const string_view &);
|
||||||
static bool select_remote(request &, const string_view &);
|
static bool select_remote(request &, const string_view &);
|
||||||
static bool select_random_remote(request &);
|
static bool select_random_remote(request &);
|
||||||
|
@ -788,281 +787,6 @@ ircd::m::fetch::finish(request &request)
|
||||||
request.promise.set_value(std::move(res));
|
request.promise.set_value(std::move(res));
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ircd::m::fetch
|
|
||||||
{
|
|
||||||
static void check_response__backfill(const request &, const json::object &);
|
|
||||||
static void check_response__event(const request &, const json::object &);
|
|
||||||
static void check_response__auth(const request &, const json::object &);
|
|
||||||
|
|
||||||
extern conf::item<bool> check_event_id;
|
|
||||||
extern conf::item<bool> check_conforms;
|
|
||||||
extern conf::item<bool> check_signature;
|
|
||||||
extern conf::item<bool> check_hashes;
|
|
||||||
extern conf::item<bool> check_authoritative_redaction;
|
|
||||||
}
|
|
||||||
|
|
||||||
decltype(ircd::m::fetch::check_event_id)
|
|
||||||
ircd::m::fetch::check_event_id
|
|
||||||
{
|
|
||||||
{ "name", "ircd.m.fetch.check.event_id" },
|
|
||||||
{ "default", true },
|
|
||||||
};
|
|
||||||
|
|
||||||
decltype(ircd::m::fetch::check_conforms)
|
|
||||||
ircd::m::fetch::check_conforms
|
|
||||||
{
|
|
||||||
{ "name", "ircd.m.fetch.check.conforms" },
|
|
||||||
{ "default", true },
|
|
||||||
};
|
|
||||||
|
|
||||||
decltype(ircd::m::fetch::check_hashes)
|
|
||||||
ircd::m::fetch::check_hashes
|
|
||||||
{
|
|
||||||
{ "name", "ircd.m.fetch.check.hashes" },
|
|
||||||
{ "default", true },
|
|
||||||
};
|
|
||||||
|
|
||||||
decltype(ircd::m::fetch::check_authoritative_redaction)
|
|
||||||
ircd::m::fetch::check_authoritative_redaction
|
|
||||||
{
|
|
||||||
{ "name", "ircd.m.fetch.check.authoritative_redaction" },
|
|
||||||
{ "default", true },
|
|
||||||
};
|
|
||||||
|
|
||||||
decltype(ircd::m::fetch::check_signature)
|
|
||||||
ircd::m::fetch::check_signature
|
|
||||||
{
|
|
||||||
{ "name", "ircd.m.fetch.check.signature" },
|
|
||||||
{ "default", true },
|
|
||||||
{ "description",
|
|
||||||
|
|
||||||
R"(
|
|
||||||
false - Signatures of events will not be checked by the fetch unit (they
|
|
||||||
are still checked normally during evaluation; this conf item does not
|
|
||||||
disable event signature verification for the server).
|
|
||||||
|
|
||||||
true - Signatures of events will be checked by the fetch unit such that
|
|
||||||
bogus responses allow the fetcher to try the next server. This check might
|
|
||||||
not occur in all cases. It will only occur if the server has the public
|
|
||||||
key already; fetch unit worker contexts cannot be blocked trying to obtain
|
|
||||||
unknown keys from remote hosts.
|
|
||||||
)"},
|
|
||||||
};
|
|
||||||
|
|
||||||
void
|
|
||||||
ircd::m::fetch::check_response(const request &request,
|
|
||||||
const json::object &response)
|
|
||||||
{
|
|
||||||
switch(request.opts.op)
|
|
||||||
{
|
|
||||||
case op::backfill:
|
|
||||||
return check_response__backfill(request, response);
|
|
||||||
|
|
||||||
case op::event:
|
|
||||||
return check_response__event(request, response);
|
|
||||||
|
|
||||||
case op::auth:
|
|
||||||
return check_response__auth(request, response);
|
|
||||||
|
|
||||||
case op::noop:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ircd::panic
|
|
||||||
{
|
|
||||||
"Unchecked response; fetch op:%u",
|
|
||||||
uint(request.opts.op),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ircd::m::fetch::check_response__auth(const request &request,
|
|
||||||
const json::object &response)
|
|
||||||
{
|
|
||||||
const json::array &auth_chain
|
|
||||||
{
|
|
||||||
response.at("auth_chain")
|
|
||||||
};
|
|
||||||
|
|
||||||
for(const json::object auth_event : auth_chain)
|
|
||||||
{
|
|
||||||
m::event::id::buf event_id;
|
|
||||||
const m::event event
|
|
||||||
{
|
|
||||||
event_id, auth_event
|
|
||||||
};
|
|
||||||
|
|
||||||
_check_event(request, event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ircd::m::fetch::check_response__event(const request &request,
|
|
||||||
const json::object &response)
|
|
||||||
{
|
|
||||||
const json::array &pdus
|
|
||||||
{
|
|
||||||
response.at("pdus")
|
|
||||||
};
|
|
||||||
|
|
||||||
const m::event event
|
|
||||||
{
|
|
||||||
pdus.at(0), request.opts.event_id
|
|
||||||
};
|
|
||||||
|
|
||||||
_check_event(request, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ircd::m::fetch::check_response__backfill(const request &request,
|
|
||||||
const json::object &response)
|
|
||||||
{
|
|
||||||
const json::array &pdus
|
|
||||||
{
|
|
||||||
response.at("pdus")
|
|
||||||
};
|
|
||||||
|
|
||||||
for(const json::object event : pdus)
|
|
||||||
{
|
|
||||||
m::event::id::buf event_id;
|
|
||||||
const m::event _event
|
|
||||||
{
|
|
||||||
event_id, event
|
|
||||||
};
|
|
||||||
|
|
||||||
_check_event(request, _event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ircd::m::fetch::_check_event(const request &request,
|
|
||||||
const m::event &event)
|
|
||||||
{
|
|
||||||
if(unlikely(!request.promise))
|
|
||||||
throw ctx::broken_promise
|
|
||||||
{
|
|
||||||
"Fetch response check interrupted."
|
|
||||||
};
|
|
||||||
|
|
||||||
if(request.opts.check_event_id && check_event_id && !m::check_id(event))
|
|
||||||
{
|
|
||||||
event::id::buf buf;
|
|
||||||
const m::event &claim
|
|
||||||
{
|
|
||||||
buf, event.source
|
|
||||||
};
|
|
||||||
|
|
||||||
throw ircd::error
|
|
||||||
{
|
|
||||||
"event::id claim:%s != sought:%s",
|
|
||||||
string_view{claim.event_id},
|
|
||||||
string_view{request.opts.event_id},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if(request.opts.check_conforms && check_conforms)
|
|
||||||
{
|
|
||||||
m::event::conforms conforms
|
|
||||||
{
|
|
||||||
event
|
|
||||||
};
|
|
||||||
|
|
||||||
const bool mismatch_hashes
|
|
||||||
{
|
|
||||||
check_hashes
|
|
||||||
&& request.opts.check_hashes
|
|
||||||
&& conforms.has(m::event::conforms::MISMATCH_HASHES)
|
|
||||||
};
|
|
||||||
|
|
||||||
const bool authoritative_redaction
|
|
||||||
{
|
|
||||||
check_authoritative_redaction
|
|
||||||
&& request.opts.authoritative_redaction
|
|
||||||
&& mismatch_hashes
|
|
||||||
&& json::get<"origin"_>(event) == request.origin
|
|
||||||
};
|
|
||||||
|
|
||||||
if(mismatch_hashes && !authoritative_redaction)
|
|
||||||
{
|
|
||||||
const json::object _unsigned
|
|
||||||
{
|
|
||||||
event.source["unsigned"]
|
|
||||||
};
|
|
||||||
|
|
||||||
const json::string redacted_by
|
|
||||||
{
|
|
||||||
_unsigned["redacted_by"]
|
|
||||||
};
|
|
||||||
|
|
||||||
if(valid(id::EVENT, redacted_by))
|
|
||||||
log::dwarning
|
|
||||||
{
|
|
||||||
log, "%s claims %s redacted by %s",
|
|
||||||
request.origin,
|
|
||||||
string_view{request.opts.event_id},
|
|
||||||
redacted_by,
|
|
||||||
};
|
|
||||||
|
|
||||||
//TODO: XXX
|
|
||||||
}
|
|
||||||
|
|
||||||
if(authoritative_redaction || !mismatch_hashes)
|
|
||||||
conforms.del(m::event::conforms::MISMATCH_HASHES);
|
|
||||||
|
|
||||||
thread_local char buf[128];
|
|
||||||
const string_view failures
|
|
||||||
{
|
|
||||||
conforms.string(buf)
|
|
||||||
};
|
|
||||||
|
|
||||||
assert(failures || conforms.clean());
|
|
||||||
if(!conforms.clean())
|
|
||||||
throw ircd::error
|
|
||||||
{
|
|
||||||
"Non-conforming event in response :%s",
|
|
||||||
failures,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// only check signature for v1 events
|
|
||||||
assert(request.promise);
|
|
||||||
if(request.opts.check_signature && check_signature && request.opts.event_id.version() == "1")
|
|
||||||
{
|
|
||||||
const string_view &server
|
|
||||||
{
|
|
||||||
!json::get<"origin"_>(event)?
|
|
||||||
user::id(at<"sender"_>(event)).host():
|
|
||||||
string_view(json::get<"origin"_>(event))
|
|
||||||
};
|
|
||||||
|
|
||||||
const json::object &signatures
|
|
||||||
{
|
|
||||||
at<"signatures"_>(event).at(server)
|
|
||||||
};
|
|
||||||
|
|
||||||
const json::string &key_id
|
|
||||||
{
|
|
||||||
!signatures.empty()?
|
|
||||||
signatures.begin()->first:
|
|
||||||
string_view{},
|
|
||||||
};
|
|
||||||
|
|
||||||
if(!key_id)
|
|
||||||
throw ircd::error
|
|
||||||
{
|
|
||||||
"Cannot find any keys for '%s' in event.signatures",
|
|
||||||
server,
|
|
||||||
};
|
|
||||||
|
|
||||||
if(m::keys::cache::has(server, key_id))
|
|
||||||
if(!verify(event, server))
|
|
||||||
throw ircd::error
|
|
||||||
{
|
|
||||||
"Signature verification failed."
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ircd::m::fetch::timedout(const request &request,
|
ircd::m::fetch::timedout(const request &request,
|
||||||
const system_point &now)
|
const system_point &now)
|
||||||
|
|
|
@ -0,0 +1,307 @@
|
||||||
|
// Matrix Construct
|
||||||
|
//
|
||||||
|
// Copyright (C) Matrix Construct Developers, Authors & Contributors
|
||||||
|
// Copyright (C) 2016-2022 Jason Volk <jason@zemos.net>
|
||||||
|
//
|
||||||
|
// 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. The
|
||||||
|
// full license for this software is available in the LICENSE file.
|
||||||
|
|
||||||
|
namespace ircd::m::fetch
|
||||||
|
{
|
||||||
|
static void check_event_signature(const request &, const m::event &);
|
||||||
|
static void check_event_conforms(const request &, const m::event &);
|
||||||
|
static void check_event_id(const request &, const m::event &);
|
||||||
|
static void check_event(const request &, const m::event &);
|
||||||
|
static void check_response__backfill(const request &, const json::object &);
|
||||||
|
static void check_response__event(const request &, const json::object &);
|
||||||
|
static void check_response__auth(const request &, const json::object &);
|
||||||
|
extern void check_response(const request &, const json::object &);
|
||||||
|
|
||||||
|
extern conf::item<bool> enable_check_event_id;
|
||||||
|
extern conf::item<bool> enable_check_conforms;
|
||||||
|
extern conf::item<bool> enable_check_signature;
|
||||||
|
extern conf::item<bool> enable_check_hashes;
|
||||||
|
extern conf::item<bool> enable_check_authoritative_redaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
decltype(ircd::m::fetch::enable_check_event_id)
|
||||||
|
ircd::m::fetch::enable_check_event_id
|
||||||
|
{
|
||||||
|
{ "name", "ircd.m.fetch.check.event_id" },
|
||||||
|
{ "default", true },
|
||||||
|
};
|
||||||
|
|
||||||
|
decltype(ircd::m::fetch::enable_check_conforms)
|
||||||
|
ircd::m::fetch::enable_check_conforms
|
||||||
|
{
|
||||||
|
{ "name", "ircd.m.fetch.check.conforms" },
|
||||||
|
{ "default", true },
|
||||||
|
};
|
||||||
|
|
||||||
|
decltype(ircd::m::fetch::enable_check_hashes)
|
||||||
|
ircd::m::fetch::enable_check_hashes
|
||||||
|
{
|
||||||
|
{ "name", "ircd.m.fetch.check.hashes" },
|
||||||
|
{ "default", true },
|
||||||
|
};
|
||||||
|
|
||||||
|
decltype(ircd::m::fetch::enable_check_authoritative_redaction)
|
||||||
|
ircd::m::fetch::enable_check_authoritative_redaction
|
||||||
|
{
|
||||||
|
{ "name", "ircd.m.fetch.check.authoritative_redaction" },
|
||||||
|
{ "default", true },
|
||||||
|
};
|
||||||
|
|
||||||
|
decltype(ircd::m::fetch::enable_check_signature)
|
||||||
|
ircd::m::fetch::enable_check_signature
|
||||||
|
{
|
||||||
|
{ "name", "ircd.m.fetch.check.signature" },
|
||||||
|
{ "default", true },
|
||||||
|
{ "description",
|
||||||
|
|
||||||
|
R"(
|
||||||
|
false - Signatures of events will not be checked by the fetch unit (they
|
||||||
|
are still checked normally during evaluation; this conf item does not
|
||||||
|
disable event signature verification for the server).
|
||||||
|
|
||||||
|
true - Signatures of events will be checked by the fetch unit such that
|
||||||
|
bogus responses allow the fetcher to try the next server. This check might
|
||||||
|
not occur in all cases. It will only occur if the server has the public
|
||||||
|
key already; fetch unit worker contexts cannot be blocked trying to obtain
|
||||||
|
unknown keys from remote hosts.
|
||||||
|
)"},
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
ircd::m::fetch::check_response(const request &request,
|
||||||
|
const json::object &response)
|
||||||
|
{
|
||||||
|
switch(request.opts.op)
|
||||||
|
{
|
||||||
|
case op::backfill:
|
||||||
|
return check_response__backfill(request, response);
|
||||||
|
|
||||||
|
case op::event:
|
||||||
|
return check_response__event(request, response);
|
||||||
|
|
||||||
|
case op::auth:
|
||||||
|
return check_response__auth(request, response);
|
||||||
|
|
||||||
|
case op::noop:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ircd::panic
|
||||||
|
{
|
||||||
|
"Unchecked response; fetch op:%u",
|
||||||
|
uint(request.opts.op),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ircd::m::fetch::check_response__auth(const request &request,
|
||||||
|
const json::object &response)
|
||||||
|
{
|
||||||
|
const json::array &auth_chain
|
||||||
|
{
|
||||||
|
response.at("auth_chain")
|
||||||
|
};
|
||||||
|
|
||||||
|
for(const json::object auth_event : auth_chain)
|
||||||
|
{
|
||||||
|
m::event::id::buf event_id;
|
||||||
|
const m::event event
|
||||||
|
{
|
||||||
|
event_id, auth_event
|
||||||
|
};
|
||||||
|
|
||||||
|
check_event(request, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ircd::m::fetch::check_response__event(const request &request,
|
||||||
|
const json::object &response)
|
||||||
|
{
|
||||||
|
const json::array &pdus
|
||||||
|
{
|
||||||
|
response.at("pdus")
|
||||||
|
};
|
||||||
|
|
||||||
|
const m::event event
|
||||||
|
{
|
||||||
|
pdus.at(0), request.opts.event_id
|
||||||
|
};
|
||||||
|
|
||||||
|
check_event(request, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ircd::m::fetch::check_response__backfill(const request &request,
|
||||||
|
const json::object &response)
|
||||||
|
{
|
||||||
|
const json::array &pdus
|
||||||
|
{
|
||||||
|
response.at("pdus")
|
||||||
|
};
|
||||||
|
|
||||||
|
for(const json::object event : pdus)
|
||||||
|
{
|
||||||
|
m::event::id::buf event_id;
|
||||||
|
const m::event _event
|
||||||
|
{
|
||||||
|
event_id, event
|
||||||
|
};
|
||||||
|
|
||||||
|
check_event(request, _event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ircd::m::fetch::check_event(const request &request,
|
||||||
|
const m::event &event)
|
||||||
|
{
|
||||||
|
if(unlikely(!request.promise))
|
||||||
|
throw ctx::broken_promise
|
||||||
|
{
|
||||||
|
"Fetch response check interrupted."
|
||||||
|
};
|
||||||
|
|
||||||
|
if(request.opts.check_event_id && enable_check_event_id)
|
||||||
|
check_event_id(request, event);
|
||||||
|
|
||||||
|
if(request.opts.check_conforms && enable_check_conforms)
|
||||||
|
check_event_conforms(request, event);
|
||||||
|
|
||||||
|
// only check signature for v1 events
|
||||||
|
assert(request.promise);
|
||||||
|
if(request.opts.check_signature && enable_check_signature && request.opts.event_id.version() == "1")
|
||||||
|
check_event_signature(request, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ircd::m::fetch::check_event_id(const request &request,
|
||||||
|
const m::event &event)
|
||||||
|
{
|
||||||
|
if(likely(m::check_id(event)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
event::id::buf buf;
|
||||||
|
const m::event &claim
|
||||||
|
{
|
||||||
|
buf, event.source
|
||||||
|
};
|
||||||
|
|
||||||
|
throw ircd::error
|
||||||
|
{
|
||||||
|
"event::id claim:%s != sought:%s",
|
||||||
|
string_view{claim.event_id},
|
||||||
|
string_view{request.opts.event_id},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ircd::m::fetch::check_event_conforms(const request &request,
|
||||||
|
const m::event &event)
|
||||||
|
{
|
||||||
|
m::event::conforms conforms
|
||||||
|
{
|
||||||
|
event
|
||||||
|
};
|
||||||
|
|
||||||
|
const bool mismatch_hashes
|
||||||
|
{
|
||||||
|
enable_check_hashes
|
||||||
|
&& request.opts.check_hashes
|
||||||
|
&& conforms.has(m::event::conforms::MISMATCH_HASHES)
|
||||||
|
};
|
||||||
|
|
||||||
|
const bool authoritative_redaction
|
||||||
|
{
|
||||||
|
enable_check_authoritative_redaction
|
||||||
|
&& request.opts.authoritative_redaction
|
||||||
|
&& mismatch_hashes
|
||||||
|
&& json::get<"origin"_>(event) == request.origin
|
||||||
|
};
|
||||||
|
|
||||||
|
if(mismatch_hashes && !authoritative_redaction)
|
||||||
|
{
|
||||||
|
const json::object _unsigned
|
||||||
|
{
|
||||||
|
event.source["unsigned"]
|
||||||
|
};
|
||||||
|
|
||||||
|
const json::string redacted_by
|
||||||
|
{
|
||||||
|
_unsigned["redacted_by"]
|
||||||
|
};
|
||||||
|
|
||||||
|
if(valid(id::EVENT, redacted_by))
|
||||||
|
log::dwarning
|
||||||
|
{
|
||||||
|
log, "%s claims %s redacted by %s",
|
||||||
|
request.origin,
|
||||||
|
string_view{request.opts.event_id},
|
||||||
|
redacted_by,
|
||||||
|
};
|
||||||
|
|
||||||
|
//TODO: XXX
|
||||||
|
}
|
||||||
|
|
||||||
|
if(authoritative_redaction || !mismatch_hashes)
|
||||||
|
conforms.del(m::event::conforms::MISMATCH_HASHES);
|
||||||
|
|
||||||
|
thread_local char buf[128];
|
||||||
|
const string_view failures
|
||||||
|
{
|
||||||
|
conforms.string(buf)
|
||||||
|
};
|
||||||
|
|
||||||
|
assert(failures || conforms.clean());
|
||||||
|
if(!conforms.clean())
|
||||||
|
throw ircd::error
|
||||||
|
{
|
||||||
|
"Non-conforming event in response :%s",
|
||||||
|
failures,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ircd::m::fetch::check_event_signature(const request &request,
|
||||||
|
const m::event &event)
|
||||||
|
{
|
||||||
|
const string_view &server
|
||||||
|
{
|
||||||
|
!json::get<"origin"_>(event)?
|
||||||
|
user::id(at<"sender"_>(event)).host():
|
||||||
|
string_view(json::get<"origin"_>(event))
|
||||||
|
};
|
||||||
|
|
||||||
|
const json::object &signatures
|
||||||
|
{
|
||||||
|
at<"signatures"_>(event).at(server)
|
||||||
|
};
|
||||||
|
|
||||||
|
const json::string &key_id
|
||||||
|
{
|
||||||
|
!signatures.empty()?
|
||||||
|
signatures.begin()->first:
|
||||||
|
string_view{},
|
||||||
|
};
|
||||||
|
|
||||||
|
if(!key_id)
|
||||||
|
throw ircd::error
|
||||||
|
{
|
||||||
|
"Cannot find any keys for '%s' in event.signatures",
|
||||||
|
server,
|
||||||
|
};
|
||||||
|
|
||||||
|
if(m::keys::cache::has(server, key_id))
|
||||||
|
if(!verify(event, server))
|
||||||
|
throw ircd::error
|
||||||
|
{
|
||||||
|
"Signature verification failed."
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in New Issue