diff --git a/recipes/pkg/jitter_sponge/README b/recipes/pkg/jitter_sponge/README new file mode 100644 index 0000000..0f5ce95 --- /dev/null +++ b/recipes/pkg/jitter_sponge/README @@ -0,0 +1,4 @@ + + Jitter Sponge + +A terminal server that provides an entropy service. diff --git a/recipes/pkg/jitter_sponge/archives b/recipes/pkg/jitter_sponge/archives new file mode 100644 index 0000000..92e0d13 --- /dev/null +++ b/recipes/pkg/jitter_sponge/archives @@ -0,0 +1 @@ +_/src/jitter_sponge diff --git a/recipes/pkg/jitter_sponge/hash b/recipes/pkg/jitter_sponge/hash new file mode 100644 index 0000000..39cdd0d --- /dev/null +++ b/recipes/pkg/jitter_sponge/hash @@ -0,0 +1 @@ +- diff --git a/recipes/pkg/jitter_sponge/runtime b/recipes/pkg/jitter_sponge/runtime new file mode 100644 index 0000000..453dc68 --- /dev/null +++ b/recipes/pkg/jitter_sponge/runtime @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/recipes/src/jitter_sponge/content.mk b/recipes/src/jitter_sponge/content.mk new file mode 100644 index 0000000..7bfc388 --- /dev/null +++ b/recipes/src/jitter_sponge/content.mk @@ -0,0 +1,23 @@ +SRC_DIR = src/server/jitter_sponge +include $(GENODE_DIR)/repos/base/recipes/src/content.inc + +MIRROR_FROM_REP_DIR = \ + lib/import/import-libkeccak.mk \ + lib/mk/libkeccak.inc \ + lib/mk/spec/32bit/libkeccak.mk \ + lib/mk/spec/64bit/libkeccak.mk \ + src/lib/keccak \ + +$(MIRROR_FROM_REP_DIR): + $(mirror_from_rep_dir) + +XKCP_PORT_DIR := $(call port_dir,$(REP_DIR)/ports/xkcp) +MIRROR_FROM_XKCP = generic32 generic64 + +generic32: $(XKCP_PORT_DIR)/generic32 + cp -r $< $@ + +generic64: $(XKCP_PORT_DIR)/generic64 + cp -r $< $@ + +content: $(MIRROR_FROM_REP_DIR) $(MIRROR_FROM_XKCP) diff --git a/recipes/src/jitter_sponge/hash b/recipes/src/jitter_sponge/hash new file mode 100644 index 0000000..39cdd0d --- /dev/null +++ b/recipes/src/jitter_sponge/hash @@ -0,0 +1 @@ +- diff --git a/recipes/src/jitter_sponge/used_apis b/recipes/src/jitter_sponge/used_apis new file mode 100644 index 0000000..0b81be2 --- /dev/null +++ b/recipes/src/jitter_sponge/used_apis @@ -0,0 +1,4 @@ +base +jitterentropy +terminal_session +vfs diff --git a/src/server/jitter_sponge/component.cc b/src/server/jitter_sponge/component.cc new file mode 100644 index 0000000..2de01e1 --- /dev/null +++ b/src/server/jitter_sponge/component.cc @@ -0,0 +1,205 @@ +/* + * \brief Jitter/Keccak entropy server + * \author Emery Hemingway + * \date 2018-11-06 + */ + +/* + * Copyright (C) 2018 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +/* local includes */ +#include "session_requests.h" + +#include +#include +#include +#include + +/* Jitterentropy includes */ +#include + +/* Keccak includes */ +extern "C" { +#include +} + + +namespace Jitter_sponge { + using namespace Genode; + + struct Generator; + class Session_component; + struct Main; + + typedef Genode::Id_space Session_space; + + struct Collection_failure : Genode::Exception { }; +} + + +struct Jitter_sponge::Generator +{ + KeccakWidth1600_SpongePRG_Instance sponge { }; + + struct rand_data *jitter = nullptr; + + void die(char const *msg) + { + /* forget sponge state */ + KeccakWidth1600_SpongePRG_Forget(&sponge); + Genode::error(msg); + throw Exception(); + } + + Generator(Allocator &alloc) + { + jitterentropy_init(alloc); + if (jent_entropy_init()) + die("jitterentropy library could not be initialized!"); + + jitter = jent_entropy_collector_alloc(0, 0); + if (!jitter) + die("failed to allocate jitter entropy collector"); + + if (KeccakWidth1600_SpongePRG_Initialize(&sponge, 254)) + die("failed to initialize sponge"); + } + + void mix() + { + /* mix at entry and exit of 'read', so 32 bytes are mixed between reads */ + enum { MIX_BYTES = 16}; + char buf[MIX_BYTES]; + if (jent_read_entropy(jitter, buf, MIX_BYTES) != MIX_BYTES) + die("jitter collection failed"); + if (KeccakWidth1600_SpongePRG_Feed(&sponge, (unsigned char *)buf, MIX_BYTES)) + die("failed to feed sponge"); + } + + void fetch(unsigned char *buf, size_t n) + { + if (KeccakWidth1600_SpongePRG_Fetch(&sponge, buf, n)) + die("failed to fetch from sponge"); + } +}; + + +class Jitter_sponge::Session_component final : public Genode::Rpc_object +{ + private: + + Session_component(Session_component const &); + Session_component &operator = (Session_component const &); + + Session_space::Element _sessions_elem; + + Genode::Attached_ram_dataspace _io_buffer; + + Generator &_generator; + + public: + + Session_component(Genode::Env &env, + Session_space &space, + Session_space::Id id, + Generator &generator) + : + _sessions_elem(*this, space, id), + _io_buffer(env.pd(), env.rm(), 0x1000), + _generator(generator) + { } + + Genode::Dataspace_capability _dataspace() { + return _io_buffer.cap(); } + + Genode::size_t _read(Genode::size_t n) + { + _generator.mix(); + n = min(n, _io_buffer.size()); + _generator.fetch(_io_buffer.local_addr(), n); + _generator.mix(); + return n; + } + + Genode::size_t read(void *, Genode::size_t) { return 0; } + + Genode::size_t _write(Genode::size_t) { return 0; } + Genode::size_t write(void const *, Genode::size_t) { return 0; } + + Size size() { return Size(0, 0); } + + bool avail() { return false; } + + void connected_sigh(Genode::Signal_context_capability cap) { + Genode::Signal_transmitter(cap).submit(); } + + void read_avail_sigh(Genode::Signal_context_capability) { } + + void size_changed_sigh(Genode::Signal_context_capability) { } +}; + + +struct Jitter_sponge::Main : Session_request_handler +{ + Genode::Env &_env; + Heap _entropy_heap { _env.pd(), _env.rm() }; + Sliced_heap _session_heap { _env.pd(), _env.rm() }; + Generator _generator { _entropy_heap }; + Session_space _sessions { }; + + void handle_session_create(Session_state::Name const &, + Parent::Server::Id pid, + Session_state::Args const &args) override + { + size_t ram_quota = + Arg_string::find_arg(args.string(), "ram_quota").ulong_value(0); + size_t session_size = + max((size_t)4096, sizeof(Session_component)); + + if (ram_quota < session_size) + throw Insufficient_ram_quota(); + + Session_space::Id id { pid.value }; + + Session_component *session = new (_session_heap) + Session_component(_env, _sessions, id, _generator); + + _env.parent().deliver_session_cap(pid, _env.ep().manage(*session)); + _generator.mix(); + } + + void handle_session_upgrade(Parent::Server::Id, + Session_state::Args const &) override { } + + void handle_session_close(Parent::Server::Id pid) override + { + Session_space::Id id { pid.value }; + _sessions.apply( + id, [&] (Session_component &session) + { + _env.ep().dissolve(session); + destroy(_session_heap, &session); + _env.parent().session_response(pid, Parent::SESSION_CLOSED); + }); + } + + Session_requests_rom _session_requests { _env, *this }; + + Main(Genode::Env &env) : _env(env) + { + env.parent().announce("Terminal"); + + /* process any requests that have already queued */ + _session_requests.schedule(); + } +}; + + +void Component::construct(Genode::Env &env) +{ + static Jitter_sponge::Main inst(env); +} diff --git a/src/server/jitter_sponge/session_requests.h b/src/server/jitter_sponge/session_requests.h new file mode 100644 index 0000000..35d5272 --- /dev/null +++ b/src/server/jitter_sponge/session_requests.h @@ -0,0 +1,137 @@ +/* + * \brief Utilities for handling the 'session_requests' ROM + * \author Emery Hemingway + * \date 2018-04-08 + */ + +/* + * Copyright (C) 2018 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef __SESSION_REQUESTS_H_ +#define __SESSION_REQUESTS_H_ + +#include +#include +#include + +namespace Genode { + struct Session_request_handler; + class Session_requests_rom; +}; + + +struct Genode::Session_request_handler : Interface +{ + virtual void handle_session_create(Session_state::Name const &, + Parent::Server::Id, + Session_state::Args const &) = 0; + virtual void handle_session_upgrade(Parent::Server::Id, + Session_state::Args const &) { } + virtual void handle_session_close(Parent::Server::Id) = 0; +}; + + +class Genode::Session_requests_rom : public Signal_handler +{ + private: + + Parent &_parent; + Session_request_handler &_requests_handler; + + Attached_rom_dataspace _parent_rom; + + void _process() + { + _parent_rom.update(); + Xml_node requests = _parent_rom.xml(); + + auto const create_fn = [&] (Xml_node request) + { + Parent::Server::Id const id { + request.attribute_value("id", ~0UL) }; + + typedef Session_state::Name Name; + typedef Session_state::Args Args; + + Name name { }; + Args args { }; + + try { + name = request.attribute_value("service", Name()); + args = request.sub_node("args").decoded_content(); + } catch (...) { + Genode::error("failed to parse request ", request); + return; + } + + try { _requests_handler.handle_session_create(name, id, args); } + catch (Service_denied) { + _parent.session_response(id, Parent::SERVICE_DENIED); } + catch (Insufficient_ram_quota) { + _parent.session_response(id, Parent::INSUFFICIENT_RAM_QUOTA); } + catch (Insufficient_cap_quota) { + _parent.session_response(id, Parent::INSUFFICIENT_CAP_QUOTA); } + catch (...) { + error("unhandled exception while creating session"); + _parent.session_response(id, Parent::SERVICE_DENIED); + throw; + } + }; + + auto const upgrade_fn = [&] (Xml_node request) + { + Parent::Server::Id const id { + request.attribute_value("id", ~0UL) }; + + typedef Session_state::Args Args; + Args args { }; + try { args = request.sub_node("args").decoded_content(); } + catch (...) { + Genode::error("failed to parse request ", request); + return; + } + + _requests_handler.handle_session_upgrade(id, args); + }; + + auto const close_fn = [&] (Xml_node request) + { + Parent::Server::Id const id { + request.attribute_value("id", ~0UL) }; + _requests_handler.handle_session_close(id); + }; + + /* close sessions to free resources */ + requests.for_each_sub_node("close", close_fn); + + /* service existing sessions */ + requests.for_each_sub_node("upgrade", upgrade_fn); + + /* create new sessions */ + requests.for_each_sub_node("create", create_fn); + } + + public: + + Session_requests_rom(Genode::Env &env, + Session_request_handler &requests_handler) + : Signal_handler(env.ep(), *this, &Session_requests_rom::_process), + _parent(env.parent()), + _requests_handler(requests_handler), + _parent_rom(env, "session_requests") + { + _parent_rom.sigh(*this); + } + + /** + * Post a signal to this requests handler + */ + void schedule() { + Signal_transmitter(*this).submit(); } +}; + +#endif diff --git a/src/server/jitter_sponge/target.mk b/src/server/jitter_sponge/target.mk new file mode 100644 index 0000000..90c640a --- /dev/null +++ b/src/server/jitter_sponge/target.mk @@ -0,0 +1,3 @@ +TARGET = jitter_sponge +LIBS += libkeccak jitterentropy base +SRC_CC += component.cc