Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 32s
56 lines
2.3 KiB
Erlang
56 lines
2.3 KiB
Erlang
-module(outbox).
|
|
-export([construct/4, sign/2, cid_of/1]).
|
|
|
|
%% Outbox envelope construction + signing per design §3.1.
|
|
%%
|
|
%% construct/4 builds an unsigned activity envelope from caller-supplied
|
|
%% (Type, ActorId, Published, Object). The envelope's `:id` field is
|
|
%% derived from the host `cid:to_string` BIF over a skeleton tag, so
|
|
%% recipients can address the activity by its content hash. The
|
|
%% returned property list is the canonical key-sorted form that
|
|
%% `envelope:canonical_bytes/1` operates on.
|
|
%%
|
|
%% sign/2 takes the unsigned envelope plus a KeySpec proplist that
|
|
%% mirrors a `public_keys` entry: `[{key_id, _}, {algorithm, _},
|
|
%% {value, KeyMaterial}]`. It computes the v1 HMAC stand-in
|
|
%% `crypto:hash(sha256, <<KeyMaterial/binary, CanonicalBytes/binary>>)`
|
|
%% — the same scheme `envelope:verify_signature/2` checks — and
|
|
%% appends a `:signature` pair.
|
|
%%
|
|
%% Real Ed25519 / RSA signing arrives in milestone 2 once
|
|
%% `crypto:sign_ed25519/2` BIFs land; the API shape doesn't change.
|
|
|
|
%% construct/4 — Type and ActorId are atoms; Published is an
|
|
%% integer timestamp the caller supplies (no clock BIF in this
|
|
%% port; the HTTP layer / outbox:publish caller injects it).
|
|
%% Object can be any term, including a property list of inner
|
|
%% fields.
|
|
construct(Type, ActorId, Published, Object) ->
|
|
Skeleton = [{actor, ActorId},
|
|
{object, Object},
|
|
{published, Published},
|
|
{type, Type}],
|
|
Id = cid:to_string({activity_envelope, Skeleton}),
|
|
[{actor, ActorId},
|
|
{id, Id},
|
|
{object, Object},
|
|
{published, Published},
|
|
{type, Type}].
|
|
|
|
%% sign/2 — KeySpec carries key_id, algorithm, value (key material).
|
|
sign(Envelope, KeySpec) ->
|
|
{ok, KeyId} = envelope:get_field(key_id, KeySpec),
|
|
{ok, Alg} = envelope:get_field(algorithm, KeySpec),
|
|
{ok, KM} = envelope:get_field(value, KeySpec),
|
|
CB = envelope:canonical_bytes(Envelope),
|
|
SigValue = crypto:hash(sha256, <<KM/binary, CB/binary>>),
|
|
Sig = [{algorithm, Alg}, {key_id, KeyId}, {value, SigValue}],
|
|
Envelope ++ [{signature, Sig}].
|
|
|
|
%% cid_of/1 — extract the :id field from a constructed envelope.
|
|
%% Convenience for callers that don't want to thread the CID
|
|
%% separately when both the envelope and its ID matter.
|
|
cid_of(Envelope) ->
|
|
{ok, Id} = envelope:get_field(id, Envelope),
|
|
Id.
|