macula_cert (macula v0.20.5)

View Source

Macula Self-Sovereign Certificate System

Provides Ed25519 DID-anchored certificate generation and verification. Unlike traditional PKI, these certificates do not require external CAs.

Certificate hierarchy: - Realm certificates are self-signed (root of trust for a namespace) - Instance certificates are signed by the realm certificate

Example usage:

Generate realm keypair and certificate: {PubKey, PrivKey} = macula_cert:generate_keypair(), {ok, RealmCert} = macula_cert:generate_realm_cert(DID, PubKey, PrivKey).

Generate instance certificate signed by realm: {InstPub, InstPriv} = macula_cert:generate_keypair(), {ok, InstCert} = macula_cert:generate_instance_cert(InstanceDID, InstPub, RealmCert, PrivKey).

Verify certificate chain: ok = macula_cert:verify_cert(InstCert, RealmCert).

Summary

Functions

Generate canonical form for signing/verification The canonical form excludes the signature field and is deterministic.

Convert common name to DID format Example: "app.io.example" becomes "did:macula:io.example.app"

Decode certificate from binary format

Convert DID to common name format Example: "did:macula:io.example.app" becomes "app.io.example"

Encode certificate to binary format Uses term_to_binary for now; can switch to CBOR/JSON for interop.

Extract realm DID from an instance DID Example: "did:macula:io.example.app.node01" becomes "did:macula:io.example"

Convert map to certificate record

Generate an instance certificate signed by a realm certificate The instance DID must be under the realm's namespace.

Generate a new Ed25519 keypair Returns {PublicKey, PrivateKey} as raw binaries.

Generate a self-signed realm certificate Realm certificates are the root of trust for a namespace.

Generate a self-signed realm certificate with custom validity

Generate a random certificate serial number

Check if a certificate is expired

Check if a certificate is currently valid (within validity period)

Sign a certificate request Used when processing CSRs from remote instances.

Convert certificate record to map

Verify an instance certificate against its issuer (realm) certificate

Verify a self-signed (realm) certificate

Types

macula_cert/0

-type macula_cert() ::
          #macula_cert{subject_did :: binary(),
                       subject_cn :: binary(),
                       issuer_did :: binary(),
                       issuer_cn :: binary(),
                       not_before :: non_neg_integer(),
                       not_after :: non_neg_integer(),
                       public_key :: binary(),
                       signature :: binary(),
                       serial :: binary(),
                       version :: pos_integer(),
                       extensions :: map()}.

macula_cert_request/0

-type macula_cert_request() ::
          #macula_cert_request{subject_did :: binary(),
                               subject_cn :: binary(),
                               public_key :: binary(),
                               validity_days :: pos_integer()}.

Functions

canonical_form(Cert)

-spec canonical_form(Cert :: macula_cert()) -> binary().

Generate canonical form for signing/verification The canonical form excludes the signature field and is deterministic.

cn_to_did(CN)

-spec cn_to_did(CN :: binary()) -> binary().

Convert common name to DID format Example: "app.io.example" becomes "did:macula:io.example.app"

decode(Binary)

-spec decode(Binary :: binary()) -> {ok, macula_cert()} | {error, term()}.

Decode certificate from binary format

did_to_cn(DID)

-spec did_to_cn(DID :: binary()) -> binary().

Convert DID to common name format Example: "did:macula:io.example.app" becomes "app.io.example"

encode(Cert)

-spec encode(Cert :: macula_cert()) -> binary().

Encode certificate to binary format Uses term_to_binary for now; can switch to CBOR/JSON for interop.

extract_realm_did(InstanceDID)

-spec extract_realm_did(InstanceDID :: binary()) -> binary().

Extract realm DID from an instance DID Example: "did:macula:io.example.app.node01" becomes "did:macula:io.example"

from_map(Map)

-spec from_map(Map :: map()) -> {ok, macula_cert()} | {error, term()}.

Convert map to certificate record

generate_instance_cert(InstanceDID, InstancePubKey, RealmCert, RealmPrivKey)

-spec generate_instance_cert(InstanceDID :: binary(),
                             InstancePubKey :: binary(),
                             RealmCert :: macula_cert(),
                             RealmPrivKey :: binary()) ->
                                {ok, macula_cert()} | {error, term()}.

Generate an instance certificate signed by a realm certificate The instance DID must be under the realm's namespace.

generate_instance_cert(InstanceDID, InstancePubKey, RealmCert, RealmPrivKey, ValidityDays)

-spec generate_instance_cert(InstanceDID :: binary(),
                             InstancePubKey :: binary(),
                             RealmCert :: macula_cert(),
                             RealmPrivKey :: binary(),
                             ValidityDays :: pos_integer()) ->
                                {ok, macula_cert()} | {error, term()}.

Generate an instance certificate with custom validity

generate_keypair()

-spec generate_keypair() -> {PublicKey :: binary(), PrivateKey :: binary()}.

Generate a new Ed25519 keypair Returns {PublicKey, PrivateKey} as raw binaries.

generate_realm_cert(RealmDID, PublicKey, PrivateKey)

-spec generate_realm_cert(RealmDID :: binary(), PublicKey :: binary(), PrivateKey :: binary()) ->
                             {ok, macula_cert()} | {error, term()}.

Generate a self-signed realm certificate Realm certificates are the root of trust for a namespace.

generate_realm_cert(RealmDID, PublicKey, PrivateKey, ValidityDays)

-spec generate_realm_cert(RealmDID :: binary(),
                          PublicKey :: binary(),
                          PrivateKey :: binary(),
                          ValidityDays :: pos_integer()) ->
                             {ok, macula_cert()} | {error, term()}.

Generate a self-signed realm certificate with custom validity

generate_serial()

-spec generate_serial() -> binary().

Generate a random certificate serial number

is_expired(Cert)

-spec is_expired(Cert :: macula_cert()) -> boolean().

Check if a certificate is expired

is_valid_now(Cert)

-spec is_valid_now(Cert :: macula_cert()) -> boolean().

Check if a certificate is currently valid (within validity period)

sign_cert_request(Request, RealmCert, RealmPrivKey)

-spec sign_cert_request(Request :: macula_cert_request(),
                        RealmCert :: macula_cert(),
                        RealmPrivKey :: binary()) ->
                           {ok, macula_cert()} | {error, term()}.

Sign a certificate request Used when processing CSRs from remote instances.

to_map(Cert)

-spec to_map(Cert :: macula_cert()) -> map().

Convert certificate record to map

verify_cert(InstanceCert, IssuerCert)

-spec verify_cert(InstanceCert :: macula_cert(), IssuerCert :: macula_cert()) -> ok | {error, term()}.

Verify an instance certificate against its issuer (realm) certificate

verify_self_signed(Cert)

-spec verify_self_signed(Cert :: macula_cert()) -> ok | {error, term()}.

Verify a self-signed (realm) certificate