Open pchiusano opened 5 years ago
you cannot write your own handler for it.
Is this necessary? Like a MITM thing?
The following compiled for me - is that bad? Looks like it would successfully steal a secret. Maybe I am cheating because a real attacker wouldn't get to write code with the t
in scope.
ex1 : Future t -> Future Text
ex1 foo =
topSecret : Pinned t Text
topSecret = pin "shouldn't leave this node"
bob : Node {Pinned.Read t}
bob = Node
Remote.at bob '(read topSecret ++ "... exfiltrating")
(Full gist here.)
@atacratic yeah, I think in practice you won’t have a node with the same token type as the pinned data.
Also aside is I think we are moving away from dealing with nodes in the API, instead there’s just logical tasks with locations / regions corresponding to some compute pool.
Playing with this some more, you need to have pin
require the ability. Also, I realized this has nothing to do with Remote
specifically, it's just a general scoping mechanism that prevents you from using tagged values outside of some lexical scope.
Here's a working example. Switching the nomenclature a bit. You'd want Local
and Scope
to be opaque for maximum safety.
unique type Local t a = Local a
unique ability Scope t where void : Void
local : a ->{Scope t} Local t a
local a = Local a
read : Local t a ->{Scope t} a
read = cases Local.Local a -> a
run : (forall t . '{Scope t, g} a) ->{g} a
run a =
handle !a with
cases
{ a } -> a
{ Scope.void -> k } -> bug "blarg"
ex = run 'let
a = local 10
b = local 12
read a + read b
foo s =
use Nat +
go s a = local (read s + a)
List.foldl go s [0,1,2,3,4,5,6]
> run '(foo (local 0) |> read)
> ex
Yields:
unique ability Scope t
unique type Local t a
ex : Nat
foo : Local t Nat ->{Scope t} Local t Nat
local : a ->{Scope t} Local t a
read : Local t a ->{Scope t} a
run : (∀ t. '{g, Scope t} a) ->{g} a
Now evaluating any watch expressions (lines starting with `>`)... Ctrl+C cancels.
30 | > run '(foo (local 0) |> read)
⧩
21
32 | > ex
⧩
22
Sometimes you want to tell Unison that data shouldn't leave a node, perhaps because it is secret. Here's a simple API for this:
So you can read a
Pinned
, but it requires thePinned.Read
ability. We can allow that ability for local programs (think like watch expressions and the like), but when creating nodes, you won't be given access to it, and since it is opaque you cannot write your own handler for it.Furthermore, we change how
Pinned
values are serialized: they are just serialized as a dummy value. Since a valid, typed program can't ever call read on the recipient node, this is fine.And an example usage:
Won't typecheck, unless
bob
node has a handler for thePinned.Read
ability which by the above reasoning won't be possible.Improving this
The above is okay, but it's pretty limited in that you can't allocate "existential" pinned data in the middle of a larger remote computation. An example: I move a computation to the
bob
node which allocates some pinned data, reads it, and then returns42
. This should be fine, since the pinned data isn't escaping, but the above API would prevent it.It seems possible to fix this using the
runST
trick, so something like:... which I believe gives you the ability to read pinned values you create locally, but not pinned values declared in outer scopes.