Skip to content

Latest commit

 

History

History
118 lines (86 loc) · 5.53 KB

UIP-0118.md

File metadata and controls

118 lines (86 loc) · 5.53 KB
uip title description author status type category created
0118
Encrypted Remote Scry
Enabling private data in the remote scry network
~hastuc-dibtux
Last Call
Standards Track
Kernel
2023-06-13

Abstract

This proposal enables private data using the remote scry protocol (Fine) by making scry requests for paths that are encrypted, so that private data can be made available using the remote scry protocol

Motivation

The basic Fine implementation does not support encryption. This prevents it from being used for a significant number of use cases, including gall subscriptions.

Specification

In order to have an encrypted remote scry, we must first define how keys are stored, distributed and assigned to paths.

Keying in ames

Ames holds an ordered map of indices to the key and the spur prefix which it protects, like so

+$  chain  ((mop ,@ud ,[key=@ vane=term =spur]) lte)

We define a new %task, to ask %ames to generate a new key for us at a path prefix.

:: inside $task:ames
[%plug vane=term =path]

For each %plug ames should give a %stub, defined as so.

:: inside $gift:ames
[%stub idx=@ud key=@]

Any pair of $ship and @ud uniquely defines a single key in the namespace that will never change, thus making it referentially transparent.

Overlay network in ames

Now that we have a key and key index, we continue by defining an overlay network inside of ames' scry handler. That is to say, we embed the entire scry namespace inside a path prefix. We use this path prefix to define key exchange before embedding the entire subpath, less the ship identifiers.

::  given a key and key index
=|  key-idx=@ud :: from %stub
=|  key=@ :: from %stub
=|  enc=$-([key=@ txt=@] @) :: encryption function, left unspecified
=/  actual-path
  /g/x/3/chat/msg/4
^-  bulk
:*  [~zod 0 1] :: ship, rift, life as usual
    [%a %x cas=[%ud p=key-idx]] :: vane is always %a, care always %x, case is used to define the key index
    /fine/shut/(scot %uv (enc key (spat actual-path)))
==

Whenever a scry is performed for this overlay network, the scry handler will retrieve the key by the key-index specified in the case, decrypt the actual path, and check that the actual path nests underneath the prefix stored in .chain. If this validation fails, the scry handler will return ~. If the validation succeeds the inner scry for actual-path is made, and the result jammed and encrypted with the key, returning ``atom+!>(@(enc (jam inner-result)))

Changes to remote scry IO

We would like to not have to re-implement the encryption logic whenever a request to this encrypted overlay network is made. Therefore, we change the type of %keen in $task:ames to

:: $task:ames
[%keen sec=(unit [idx=@ key=@]) spar]

However, this now produces another usability problem, because the type of %tune (the result $gift:ames) produces a signature along with the datum. If we keep this interface, our choices are to either handle decryption in the calling vane or produce an invalid and meaningless signature. Both of these options are unacceptable, therefore we introduce %near, which is just a %tune with the signature information discarded.

:: $gift:ames
[%near spar dat=(unit (unit page))]

Gall changes

This introduces usability issues for gall agents, which is that the key exchange must be implemented in the agent. We address this by introducing a new case in $note:agent:gall, removing the need to specify the key ahead of time, moving key exchange for agents into gall itself. Agents should always use this, instead of passing an [%arvo %a %keen].

:: $sign:agent:gall
[%keen sec=? spar]

In order for this interface to be made meaningful, we are required to define operations so that gall can perform key exchange on the agents behalf. To do this, we introduce the notion of a $coop

:: inside +gall (sys/lull.hoon)
+$  coop  spur

The coop is gall's opinionated take on the path prefixing system introduced with %plug. It defines a security context for a gall agent's namespace, where everything bound underneath this spur is encrypted by the same key. We now must define a lifecycle for a %coop

::  +sign:agent:gall
[%germ =coop] :: create coop
[%snip =coop] :: delete coop

This is a simple wrapper over the %plug/%stub interface. If a %germ is passed ot an already extant $coop, then gall will rotate the key for the existing $coop. Next we need to define what ships have privileges for the $coop. This is done by way of scrying into the agent with care %c, where the path is the product of (snoc coop (scot %p requesting-ship)). This is required to return ?, indicating whether the ship has permission for the $coop. Returning any other type of data, crashing or producing a ~ or [~ ~] will be the same as returning |. Key exchange is done as a simple %plea/%boon flow.

(on-peek:agent (snoc coop (scot %p requesting-ship)))

If the agent intends to produce | for a ship where it has previously produced & then it is required to produce a %germ to invalidate the key that the newly depermissioned ship has. All that is left now is to allow for gall agents to bind into this encrypted namespace.

$note:agent:gall
[%tend =coop =path =page]

This is analogous to %grow, except the spur has been split into $coop and $path, so that the calling agent is forced to be specific about the security context that the $page belongs to. %tomb and %cull work as expected with paths that have been created with %tend.