Distributed Security
Within a single address-space, the capability-security properties of actor references are guaranteed by the actor run-time. Memory-safe implementations of actor languages ensure that actor references cannot be forged. Having a reference to an actor means you have permission to send it a message. A single machine may host multiple independent actor address-spaces, each of which is a separate actor configuration. This is what Tristan Slominski calls the Memory Realm.
We would like actors to communicate and collaborate across configurations. When a local actor within a configuration must be referenced from another configuration, we call that actor a receptionist. From the perspective of each referencing configuration, this is an external actor [1]. In order to support references that span across multiple configuration address-spaces, we need a new kind of designation. We substitute unguessability for unforgeability and assign a randomly chosen swiss number (as Mark Miller et. al. called it) to each receptionist [2]. We choose this number from a large enough pool that we can be confident that the designation uniquely identifies a single actor without collisions. This gives us a single virtual address-space for all actor references, so we can extend our definition of equal globally [3]. If two designations are equal, they must designate the same actor [4]. Tristan Slominski calls this the Trusted Realm.
Proxies and Stubs
An actor becomes a receptionist when a reference to it is exported from its host configuration. At that point, a stub is created that can deliver messages to the actor. The stub is not visible from inside the configuration. It exists only to represent the receptionist outside the configuration.
When an actor reference is imported into another configuration, a proxy is created that represents the external actor. The proxy is visible from inside the configuration. In fact, it is indistinguishable from a local actor. It exists to translate messages into a form that can be transported to the remote configuration hosting the target actor.
Figure 1 shows an initial actor configuration where Alice has references to Bob and Carol. The reference to Carol is simply a direct reference to a local actor. The reference to Bob begins with a proxy, in Configuration X, representing Bob as an external actor. The proxy, in turn, uses the globally-unique virtual address of Bob to transport messages across a private connection between configurations. The stub for Bob, on Configuration Y, has a direct reference for delivering messages to Bob.
In Figure 2 Alice is sending a message to introduce Carol to Bob. From Alice’s perspective, this is no different than sending a message to the local actor Carol. As usual, all actor messages are asynchronous and travel over one-way references, so a delay in delivering Alice’s message does not change the semantics. Note also the very real possibility that messages may arrive in a different order than they are sent (or not at all). This is simply the nature of physical communication.
In Figure 3 the proxy for Bob translates the message (a reference to Carol) into a serialized format for transport between configurations. This is usually called “marshalling”. In the process, Carol becomes a receptionist, so Configuration X creates a stub for Carol. The marshalled message contains a reference to this stub. Note that Alice is not a receptionist. Since there are (currently) no external references to Alice, no stub is required.
In Figure 4 the stub for Bob receives the marshalled message containing the virtual address of Carol. Since Carol was not previously known, Configuration Y creates a proxy to represent Carol locally. The message is unmarshalled (or deserialized), translating Carol’s virtual address into a reference to this proxy. The resulting message is delivered to Bob.
And thus the connectivity graph of actors grows by successive introductions. Proxies for external actors and stubs for receptionists are created by configurations as needed to support the evolving topology. Each actor sees only its collection of opaque actor references, each representing the capability to send asynchronous messages to the designated actor. The tart-marshal project demonstrates a working example of this scheme implemented in NodeJS.
Beyond the Circle of Trust
We would like actor configurations to be able to safely collaborate without assuming that they trust each other, or the network connecting them. Since actor designation implies access authorization, we must take steps to protect the designations from intermediaries (like the network/routing system components) which have no need-to-know the designation. This is what Tristan Slominski calls the Public Realm.
In order to communicate securely across untrusted channels, we wrap each message in an opaque envelope (securing the contents) and place a label on the outside which provides routing information for the target actor. The routing information does not, by itself, provide authorization to send a message to the target. It must be combined with authorization to open the envelope in order to deliver the message [5].
Each actor configuration creates a public/private key-pair \(\langle e, d \rangle\) to allow encryption and decryption of messages. This is operationally equivalent to a matched pair of seal and unseal functions [6]. The payload \(p\) can only be read if you hold the decryption (private) key.
$$
p = d(e(p))
$$
A secure sturdy-reference to a receptionist consists of the encryption (public) key of the host configuration and the swiss number of the actor \(\langle e, a \rangle\). This information is sufficient to designate, and therefore authorize access to, a unique actor. A sender will use \(e\) to seal an envelope containing the target actor reference and the message.
$$
s = e(\langle\langle e, a \rangle, m\rangle)
$$
However, we do not trust the network/routing system with \(e\). The label on the sealed envelope is a cryptographic hash of the encryption key \(r = h(e)\) so the network only sees \(\langle r, s \rangle\). This means that the network must be able to route the sealed envelope \(s\) using only the routing hint \(r\). The envelope can only be unsealed by the appropriate decryption key (held privately by the target configuration), and the routing hint can not be used to reveal the encryption key.
When the sealed envelope is presented to the host-configuration designated by the routing hint, the configuration uses the decryption (private) key \(d\) to recover the contents of the envelope, which will be the target actor reference and the message.
$$
\langle\langle e, a \rangle, m\rangle = d(s)
$$
If the envelope can be opened, we can be certain that it was destined for the receiving configuration. The sender is assured that the message can only be read by the designated recipient. No one who observes the message in transit \(\langle r, s \rangle\) will be able to use this information to send an unsolicited message, nor will they be able to tamper with the original message without detection. All receptionist references are protected from exposure, including the target actor and any designations contained in the message itself.
Principle of Least Authority
Authority should be provided only on a need-to-do basis. Each component in a system should be given no more power (or knowledge) than it requires to perform its designated function. This is easier to accomplish when there is good separation-of-concerns among components.
The network/routing system is responsible for transporting opaque (encrypted) blobs to their proper destination. To accomplish this, each configuration is given a unique designation. However, that designation does not confer the authority to send an arbitrary message, only to deliver a blob to a host. The network/routing system maintains mappings from configuration designations (called routing hints above), to network endpoints which host configurations. This implies that the unit of mobility is the configuration, rather than the actor. A configuration may persist across host restarts, or even move among host machines.
Each configuration is responsible for authenticating inbound messages and delivering them to the designated receptionist actors. Blobs that cannot be authenticated (by decryption) are not delivered. They are also responsible for maintaining the encryption keys needed to secure outbound messages for configurations hosting external actors, and giving the resulting labelled envelopes to the network/routing system.
Actors are responsible for performing computations based on asynchronous messages they receive. External actors, receptionists, and internal actors are all designated by unforgeable references. Actors are never exposed to the external representations of their references. The directed-graph of actor references places an upper bound on the authority available to each actor (potentially attenuated by the behavior of intervening actors).
Summary
Actors can directly implement Object Capability Security (ocaps). This provides a strong basis for reasoning about distributed security properties. Using these reusable mechanisms, we can design secure protocols among mutually suspicious participants [7]. Each component of the system is given only the authority required to accomplish its design goals. Security is achieved by controlling access to powerful objects, represented as actor references.
References
- [1]
- G. Agha , I. Mason , S. Smith , C. Talcott. Towards a Theory of Actor Computation. Proceedings of the Third International Conference on Concurrency Theory pp.565-579. August 1992.
- [2]
- M. Miller. Cryptographic Capabilities. Capabilities As A Cryptographic Protocol, Retrieved 2017-02-07.
- [3]
- H. Baker. Equal Rights for Functional Objects or, The More Things Change, The More They Are the Same. ACM OOPS Messenger 4(4):2-27. October 1993.
- [4]
- M. Miller. The Grant Matcher Puzzle, Retrieved 2017-02-07.
- [5]
- M. Miller. Rights Amplification. From Objects To Capabilities, Retrieved 2017-02-07.
- [6]
- J. H. Morris. Protection in Programming Languages, Communications of the ACM 16(1):15-21, 1973.
- [7]
- M. Miller, C. Morningstar, B. Frantz. Capability-based Financial Instruments. Proceedings of the 4th International Conference on Financial Cryptography pp.349-378. Springer-Verlag, Anguila, BWI, February 2000.
Tags: actor, authentication, authorization, capability, decryption, distribution, encryption, envelope, network, ocaps, POLA, proxy, receptionist, routing, sealer, stub, unsealer
A very accessible discussion of the Principle Of Least Authority (POLA) and how it could mitigate damage caused by security violations. https://medium.com/agoric/pola-would-have-prevented-the-event-stream-incident-45653ecbda99