macula_foundation (macula v11.1.0)

View Source

Foundation trust anchor: the key ids of the foundation keys a node trusts, and the check of a foundation record against them.

The Foundation signs Tier A seed lists, protocol parameters, realm trust lists and T3 attestations (Part 6 §9.14 to §9.17). A foundation record carries one foundation signature in the signed-object format, by a key whose purpose is foundation. A node trusts foundation keys by key id: SHA-256 over MACULA-KEY-ID-V1, the profile and the key as carried (macula_node_keys:key_id/2). The trusted key ids are the root of trust for the Tier A bootstrap path (Part 5 §4).

Configuration

Production deployments set the trusted key ids in the macula application environment, under foundation_key_ids, before any Tier A record is trusted. Replacing a key id takes a configuration or firmware update; records signed by a retired key stay valid until they expire.

Placeholders

Without that setting, key_ids/0 returns five deterministic placeholders, SHA-256 digests of fixed labels. No key derives to a placeholder, so no record verifies as signed by one. Callers that must work only against configured keys use live_key_ids/0, which returns the configured key ids or an empty list, never the placeholders.

Reference: plans/PLAN_MACULA_V2_PART5_BOOTSTRAP.md §4, §12; plans/PLAN_MACULA_V2_PART6_PROTOCOL.md §9.14 to §9.17; plans/PLAN_POST_QUANTUM_SECURITY.md.

Summary

Functions

Whether a value is one of the trusted foundation key ids.

The trusted foundation key ids: the configured ones when foundation_key_ids is set and not empty, the placeholders otherwise, for development and tests only.

The configured foundation key ids, or an empty list; never the placeholders. Production bootstrap paths use it to refuse to trust a record that only a placeholder would admit.

The placeholder key ids: SHA-256 digests of fixed labels, which no key derives to.

Whether the node trusts the placeholder key ids, because no foundation key id is configured. Production code refuses to bootstrap while this is true.

Verify a foundation record, as its wire form or its {key, tbs, signature} map, under the verifier's profile. The record is accepted when it verifies (macula_record:verify/3), its type is a foundation type, and its signer's key id is trusted. Refusals are returned in that order, never raised.

As verify_record/2, at the verifier's clock Now, in milliseconds.

Types

key_id/0

-type key_id() :: <<_:256>>.

verify_error/0

-type verify_error() ::
          record_too_large | malformed | signature_invalid | alg_mismatch | not_yet_valid | expired |
          key_id_mismatch | wrong_type | not_foundation_signed.

Functions

is_foundation(KeyId)

-spec is_foundation(term()) -> boolean().

Whether a value is one of the trusted foundation key ids.

key_ids()

-spec key_ids() -> [key_id()].

The trusted foundation key ids: the configured ones when foundation_key_ids is set and not empty, the placeholders otherwise, for development and tests only.

live_key_ids()

-spec live_key_ids() -> [key_id()].

The configured foundation key ids, or an empty list; never the placeholders. Production bootstrap paths use it to refuse to trust a record that only a placeholder would admit.

placeholder_key_ids()

-spec placeholder_key_ids() -> [key_id()].

The placeholder key ids: SHA-256 digests of fixed labels, which no key derives to.

placeholder_mode()

-spec placeholder_mode() -> boolean().

Whether the node trusts the placeholder key ids, because no foundation key id is configured. Production code refuses to bootstrap while this is true.

verify_record(Signed, Profile)

-spec verify_record(binary() | map(), macula_crypto_profile:profile()) ->
                       {ok, macula_record:m_record()} | {error, verify_error()}.

Verify a foundation record, as its wire form or its {key, tbs, signature} map, under the verifier's profile. The record is accepted when it verifies (macula_record:verify/3), its type is a foundation type, and its signer's key id is trusted. Refusals are returned in that order, never raised.

verify_record(Signed, Profile, Now)

-spec verify_record(binary() | map(), macula_crypto_profile:profile(), integer()) ->
                       {ok, macula_record:m_record()} | {error, verify_error()}.

As verify_record/2, at the verifier's clock Now, in milliseconds.