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