We can implement access control in goose-lean using the following building blocks.
cryptographic key pairs of a public and a private key
signatures are the principal authentication mechanism
access lists as immutable fields/slots in objects
In “easy mode”, at object creation, the access control list is given as part of the constructor argument, potentially at the granularity of methods. So, for each method, we add the check of a suitable signature is provided.[2] If this looks suspiciously easy: the answer may be related to the fact that the property of access control is a safety property whereas information flow is a non-trivial hyperproperty, i.e., a property over all runs of a system. That shall be the topic of another post.
The original speaks about access control matrices, but access control list is the more well-known word, I think. Also, there’s a nit to address: of course, this property should not be called access control but access compliance, but well, let us get to the point … ↩︎
It turns out that we can represent more sophisticated access control patterns already in the current version of goose-lean, allowing for mutability of access control lists. ↩︎
A design note: in my view, we should implement access control in a way which is intentionally agnostic as to whether the authorization in question is performed by an agent (with private information) or another object (without it) – otherwise we’d need two separate systems for these two cases, which seems more time-consuming to design and inelegant to use.
I sketched out a bit on this direction in this thread, but it may not be sufficiently clear to proceed – I’m happy to discuss it further, but I strongly recommend that we do not start implementing a system which deals only in cryptographic signatures and does not support objects themselves as authorizors, as I suspect we would have to ditch that system later anyways.
Indeed, that is the core idea of any lattice-based approach.
For clarification: the main message that should have been added to summarize the post is that, using the current version of goose-lean, access control can be implemented to ensure the relevant information flow property despite the fact that we do not have yet any access control specific syntax.
The most basic thing we need to implement access control is for the receiver to be able to check the identity of the caller – do we have that in current Goose?
The most basic thing we need to implement access control is for the receiver to be able to check the identity of the caller – do we have that in current Goose?
Yes, based on general public-key cryptography. The caller can sign the message with their private key. Then the receiver can check if the message was signed by the caller using the caller’s public key (which they need to know). We have some abstractions and mock functions in the application layer to make this a bit more user-friendly.
Do we also have the ability for the receiver to check the identity of the calling object? [e.g. for a particular “balance” object to check that it is being called by the “bank” object which created it]
Do we also have the ability for the receiver to check the identity of the calling object? [e.g. for a particular “balance” object to check that it is being called by the “bank” object which created it]
Hm, not directly. The problem with this would be how to handle private keys so that they remain private. If an object, not a user, “owns” the private key, is it stored in the resource corresponding to the object? If so, then it’s not private because anyone can fetch the object resource from the RM. I don’t know how to combine the two paradigms. Someone needs to sign the message with a private key which is not published anywhere, so it seems the private key needs to be associated with whoever does the signing. It thus cannot be associated with an object if multiple users can submit transactions which implement e.g. the “bank” object calling a method of a “balance” object (with the “bank” object being the same in both cases).
The mismatch is that objects are abstractions which don’t actually physically run anything or submit transactions.
Maybe we could assume that whoever submits transactions for “bank” has access to a private key for bank, which is shared with authorized users by other means. But I don’t know if that’s a good model we want to pursue.
Ah, to be clear – I’m not suggesting that we want to store private keys in objects (I agree that we do not, and with all of the problems which you mention). By “identity” in this case I meant something like “object identifier” (of the object which sent a particular message).
I don’t think we can do it in a way that is resistant to attacks - because that would seem to require some cryptography in the way I outlined above, with a private key associated with an object not a user. We can of course store an sender id in the message (we don’t do it now, but we could in principle), and then check it, but without cryptographic signing there seems to be no guarantee that the sender id stored in the message actually corresponds to the sender object.
Why not? Can’t we encode checks in the resource logic for resources representing messages that the message with sender identity S can only be created in an action with an object resource with identity S, or something like this?
Thanks – makes sense, and I do think we should add it. A simple example could just be a counter owned by another object (which then checks the identity of the calling object in the increment method). Permissioning check destruction to the Bank object with which a check is associated could be another example.
Summarizing: we should have the sender id in the messages, correctness of which is enforced by the RL of the message on creation, and the balance check for consumed messages. Once we have that, we introduce the possibility for the user to check the sender id in their methods / invariants. Then we can replace (some) multi-methods by ordinary methods with appropriate sender checks.