============= Example Usage ============= .. _grant-capabilities: Create Grant Capabilities ========================= The most basic use of the CAProck library is to create capabilities to transmit to other processes or networked nodes. Grantor ------- These capabilities usually grand access to some object, and objects usually don't float freely in cyberspace, but have an "owner" of sorts - or someone who can make authoritative statements about who may have access. We'll call this the `issuer` or `grantor`, and they're represented by a cryptographic key pair. Let's generate an Edwards curve key, because they're hip and small. .. code-block:: shell % openssl genpkey -algorithm ed25519 -out grantor.pem For this example, it does not matter how you load this file. It's sufficient that the library gets it as a string. Let's hard-code it for the time being. .. warning:: Do not hard-code key data in production code. It's one of the most embarrassing security issues imaginable. You don't want to be embarrassed, do you? So this is how it should **not** look in your code: .. code-block:: c :linenos: char const * key = "-----BEGIN PRIVATE KEY-----..."; We can now create a key pair from this input data: .. code-block:: c :linenos: #include caprock_key_pair kpair = caprock_key_init_pair(); caprock_error_t err = caprock_key_read_pair(&kpair, key, strlen(key), NULL); assert(CAPROCK_ERR_SUCCESS == err); Grantee ------- Capabilities transfer rights `to` someone as well, which means it's necessary to identify them (as the grantee). The assumption is that authentication also occurs via a cryptographic challenge based on public keys, which means we have at least a public key when we know a grantee. We can create an identifier from such a public key. .. code-block:: c :linenos: caprock_key * pubkey = NULL; /* initialize pubkey much like the above */ char grantee_buf[100]; size_t grantee_buf_size = sizeof(idbuf); caprock_error_t err = caprock_key_identifier_public( CFI_CLAIMv1_SUBJECT, grantee_buf, &grantee_buf_size, pubkey, CIH_ANY); assert(CAPROCK_ERR_SUCCESS == err); Objects ------- Object identifiers don't really have any specific format. However, identities can also be the *object* of a permission. For example, I could permit a person A to open the door to person B; in that case, the identifier for person B would be the object of a capability's claim. For that reason, it's easiest to treat object identifiers as being in the same namespace as subject identifiers. Which means we can *hash* any arbitrary object name into an identifier. .. code-block:: c :linenos: #include char object_buf[100]; size_t object_buf_size = sizeof(idbuf); caprock_error_t err = caprock_create_object_id( object_buf, &object_buf_size, "test name", 0, CIH_512); assert(CAPROCK_ERR_SUCCESS == err); Create Capability ----------------- Once we have the identities sorted, we need to create one or more claims about the grantees and objects. Note that each capability may contain several claims, but we're only using a single one here. The *privilege* or *predicate* is entirely application defined, so go wild! Use any string that expresses how you want to explain the relationship between the grantees and objects. .. code-block:: c :linenos: caprock_claim claim = { grantee_buf, grantee_buf_size, "my example privilege", 0, /* 0 here means to use strlen() */ object_buf, object_buf_size, }; char capability[500]; size_t capability_size = sizeof(capability); caprock_error_t err = caprock_grant_create( capability, &capability_size, kpair, 1, /* Sequence number */ "2023-06-26T16:51:00Z", /* from */ "2023-06-27T16:51:00Z", /* to */ CAPROCK_EXPIRY_POLICY_ISSUER, &claim, 1, CIH_ANY, CSA_AUTO); assert(CAPROCK_ERR_SUCCESS == err); That's it. Though it's probably worth explaining some of the other parameters here. #. First of all, the sequence number applies to the issuer. That is, in order to determine in which sequence to process a number of capabilities by the same issuer, capabilities are typically sorted by sequence number. So every issuer needs to strictly increment this number for every capability created. If this reminds you how a certificate authority works, that's no coincidence. #. The ``from`` and ``to`` parameters specify the capability's validity period, expressed as ISO-8601 string dates. #. You will *usually* want to pass :var:`CAPROCK_EXPIRY_POLICY_ISSUER` in the policy parameter. #. Pass a pointer to the first claim, and the number of claims to process next. #. Let the implementation choose a signature hash type with :var:`CIH_ANY`. #. Let the implementation choose a signature algorithm with :var:`CSA_AUTO`. Create Revoke Capabilities ========================== Creating a revoke capability follows the same logic as :ref:`creating a grant capability `. The only difference is that instead of using :func:`caprock_grant_create`, you instead call :func:`caprock_revocation_create`. Note that revocations typically negate a previously issued grant. This means the sequence number of a revocation will usually be higher than that of a corresponding grant. It's possible to issue revocations before issuing a grant, but they don't have any particular effect. .. warning:: It's also possible to create revocations for different time spans than grants, effectively "punching a hole" in a grant. This is part of the design, but should not be abused. .. _validate-capabilities: Validate a Capability ===================== Validating a capability involves checking whether its signature is correct. But it also asks whether the capability makes any kind of sense at the current point in time. .. code-block:: c :linenos: char capability[] = { ... }; caprock_error_t err = caprock_token_validate( capabiltiy, sizeof(capability), "2023-07-01T14:31:00Z", public_key); assert(CAPROCK_ERR_SUCCESS == err); The public key provided here must be that of an authority known to issue capabilities. Validate a Claim ================ While :ref:`validating a capability ` is certainly possible, the most common use case for capabilities is also somewhat more complex. What you typically need to do is check whether a kind of access that was requested is permitted at the current point in time. From a CAProck perspective, you're validating a *claim* against a *set of capabilities*. Since the library does not know -- or care to know -- how you might store your capabilities, it offers an iterator construct for producing all the capabilities your software knows. You can use this iterator to e.g. query a database or some such. .. code-block:: c :linenos: caprock_error_t my_iterator(void * buffer, size_t * bufsize, void * baton) { // The baton contains a context for the iterator, such as e.g. a database // handle, etc. my_db * handle = (my_db *) baton; my_db->write_next_capability_to_buffer(buffer, bufsize); if (bufsize > 0) { return CAPROCK_ERR_SUCCESS; } return CAPROCK_ERR_END_ITERATION; } The above code sample involves the use of some imaginary database handle that writes capabilities into an output buffer, provided to the iterator function. Of course, this is effectively pseudo-code, and you'll have to provide your own implementation. Suffice to know that the *best* iterator produces the capabilities in order of ascending sequence numbers. At the same time, the iterator can produce only those capabilities that are signed by the same authority; this is something of an optimization, however. When you use the iterator, you typically deal with a request for some resource, specified by an object identifier. You'll also know who is requesting access, from a prior authentication phase. Finally, the request provides you with information on how the resource access should occur. All of this together means you can construct a *claim*. .. code-block:: c :linenos: caprock_claim request = { subject, sizeof(subject), predicate, sizeof(predicate), object, sizeof(object), }; We initialize the imaginary database query; in this case we query for capabilities related to the given object. .. code-block:: c :linenos: my_db * handle = query_db_with_caps_about(object); What is left to do is to check whether the claim is permitted or forbidden by the set of capabilities that the iterator provides. .. code-block:: c :linenos: // We provide the scratch buffer to the function. char tmp[1000]; caprock_error_t err = caprock_claim_validate( &request, 1, // a single request/claim is checked "2023-07-02T07:38:12Z", // the current time tmp, sizeof(tmp), verifier_pubkey, CIF_NONE); assert(CAPROCK_ERR_SUCCESS == err); On success, we can now state that the capabilities the iterator provides allow the resource access requested.