macula_statement_issuer (macula v11.5.0)

View Source

A node's status statement issuer (DESIGN_PQ_HANDSHAKE_FRAMES.md, Binding and Status statement; D22).

It holds the identity key, the node's CONNECT keys and the TLS leaves its owner registers, a binding for each and a fresh status statement for each binding.

Every 15 minutes it issues a statement valid for 1 hour for each binding whose not_after has not passed, and sends it to the processes subscribed to that binding. Every 5 days it rotates the CONNECT key: the new binding and its statement exist before connect_material/1 hands the key out, and the rotated-out binding keeps its statements until its not_after. Every 5 days it also tells its owner a TLS rotation is due. The owner makes the TLS key and certificate, and register_tls_leaf/3 returns only once the leaf's binding and statement exist, so the owner reloads its listener after that. When no newer TLS binding exists 24 hours before the newest one's not_after, the issuer emits a warning diagnostic at every check until one does.

With a key directory, CONNECT keys and registered TLS keys are saved with their bindings, and a restarted issuer reloads the live ones and keeps issuing statements for them. Without one they live in memory only, and a restarted issuer starts with a new CONNECT key and binding. The identity key is never written. A key directory the issuer makes, and every file it saves there, is readable by its owner only.

Summary

Functions

The current CONNECT key with its binding and a fresh status statement, for a new dial.

Bind a TLS leaf the owner made, with its TLS key. Returns once the binding and its statement exist, so the owner can have its listener present the leaf after that.

Send the caller {macula_statement, Issuer, BindingHash, Statement} at every reissue for the binding whose tbs hashes to BindingHash, until the caller exits or the binding's not_after passes.

Run the periodic work now, at the issuer's clock: drop expired bindings, reissue statements, rotate the CONNECT key when due, and check the TLS rotation.

The binding and fresh status statement for the leaf whose SHA-384 is LeafHash.

Types

connect_material/0

-type connect_material() ::
          #{connect_key := macula_node_keys:node_key(),
            connect_binding := map(),
            connect_status := map()}.

options/0

-type options() ::
          #{identity := fun(() -> macula_node_keys:node_key()),
            owner := pid(),
            key_dir => file:name_all(),
            clock => fun(() -> non_neg_integer())}.

tls_material/0

-type tls_material() :: #{tls_binding := map(), tls_status := map()}.

Functions

connect_material(Issuer)

-spec connect_material(pid()) -> connect_material().

The current CONNECT key with its binding and a fresh status statement, for a new dial.

format_status(Status)

handle_call(_, From, S)

handle_cast(Message, S)

handle_info(Message, S)

init(Options)

register_tls_leaf(Issuer, LeafDer, TlsKey)

-spec register_tls_leaf(pid(), binary(), macula_node_keys:node_key()) -> ok.

Bind a TLS leaf the owner made, with its TLS key. Returns once the binding and its statement exist, so the owner can have its listener present the leaf after that.

start_link(Options)

-spec start_link(options()) -> {ok, pid()} | {error, term()}.

subscribe(Issuer, BindingHash)

-spec subscribe(pid(), <<_:384>>) -> ok | {error, unknown_binding}.

Send the caller {macula_statement, Issuer, BindingHash, Statement} at every reissue for the binding whose tbs hashes to BindingHash, until the caller exits or the binding's not_after passes.

tick(Issuer)

-spec tick(pid()) -> ok.

Run the periodic work now, at the issuer's clock: drop expired bindings, reissue statements, rotate the CONNECT key when due, and check the TLS rotation.

tls_material(Issuer, LeafHash)

-spec tls_material(pid(), <<_:384>>) -> {ok, tls_material()} | {error, unknown_leaf}.

The binding and fresh status statement for the leaf whose SHA-384 is LeafHash.