Speedup and cleanup vom encoder, some bugfixes.

This brings the vom Encoder implementation into a reasonable
state, performance-wise.  The technique is the same as on the
decoder side:
https://vanadium-review.googlesource.com/#/q/topic:toddw-fastdec

Also fixed a bug in the handling of bytes in vdl.newPipe.  Added
a bunch of comments in v.io/v23/vdl/pipe.go to describe the
general mechanism.

The important files to look at are under:
  v.io/v23/vdl
  v.io/v23/vom
  v.io/v23/verror
  v.io/x/ref/lib/vdl/codegen/golang

Here are the encoding benchmarks.  Note that there is higher
variance for these numbers than the decoder numbers, since
encoding requires allocations on the output buffer.  Our solution
to that will be chunked encoding, which isn't implemented here.

benchmark                          old ns/op  new ns/op    delta
Vom___Encode_____XNumber                6647       4216  -36.57%
Vom___EncodeMany_XNumber                 472        428   -9.32%
Vom___Encode_____VNumber                6482       5542  -14.50%
Vom___EncodeMany_VNumber                 176        174   -1.14%
Vom___Encode_____XStringSmall           6862       6531   -4.82%
Vom___EncodeMany_XStringSmall            527        460  -12.71%
Vom___Encode_____VStringSmall           6257       5685   -9.14%
Vom___EncodeMany_VStringSmall            196        188   -4.08%
Vom___Encode_____XStringLarge         133510     120660   -9.62%
Vom___EncodeMany_XStringLarge          55130      52237   -5.25%
Vom___Encode_____VStringLarge         130751     129680   -0.82%
Vom___EncodeMany_VStringLarge          53851      51242   -4.84%
Vom___Encode_____VEnum                  7093       6172  -12.98%
Vom___EncodeMany_VEnum                   191        183   -4.19%
Vom___Encode_____XByteListSmall         7351       6873   -6.50%
Vom___EncodeMany_XByteListSmall          579        472  -18.48%
Vom___Encode_____VByteListSmall         6623       6157   -7.04%
Vom___EncodeMany_VByteListSmall          187        177   -5.35%
Vom___Encode_____XByteListLarge       104355     104017   -0.32%
Vom___EncodeMany_XByteListLarge         8676       8696   +0.23%
Vom___Encode_____VByteListLarge        97597     100401   +2.87%
Vom___EncodeMany_VByteListLarge         8052       8231   +2.22%
Vom___Encode_____XByteArray             7364       7530   +2.25%
Vom___EncodeMany_XByteArray              745        749   +0.54%
Vom___Encode_____VByteArray             6595       6139   -6.91%
Vom___EncodeMany_VByteArray              215        202   -6.05%
Vom___Encode_____XArray                 8624       7307  -15.27%
Vom___EncodeMany_XArray                 1266        630  -50.24%
Vom___Encode_____VArray                 7358       6523  -11.35%
Vom___EncodeMany_VArray                  370        274  -25.95%
Vom___Encode_____XListSmall             8184       6991  -14.58%
Vom___EncodeMany_XListSmall             1505        688  -54.29%
Vom___Encode_____VListSmall             7000       6308   -9.89%
Vom___EncodeMany_VListSmall              390        288  -26.15%
Vom___Encode_____XListLarge         21629416    4103590  -81.03%
Vom___EncodeMany_XListLarge         20971508    3440893  -83.59%
Vom___Encode_____VListLarge          4816700    2821262  -41.43%
Vom___EncodeMany_VListLarge          4226822    2257347  -46.59%
Vom___Encode_____XListAnySmall         20538      20456   -0.40%
Vom___EncodeMany_XListAnySmall         11473      10555   -8.00%
Vom___Encode_____VListAnySmall         17542      13722  -21.78%
Vom___EncodeMany_VListAnySmall          9502       5963  -37.24%
Vom___Encode_____XListAnyLarge     242633472  186445853  -23.16%
Vom___EncodeMany_XListAnyLarge     231676344  184135888  -20.52%
Vom___Encode_____VListAnyLarge     243393646  232625450   -4.42%
Vom___EncodeMany_VListAnyLarge     204297618  208020229   +1.82%
Vom___Encode_____VSet                   7379       6520  -11.64%
Vom___EncodeMany_VSet                    539        435  -19.29%
Vom___Encode_____XMap                  10599       9060  -14.52%
Vom___EncodeMany_XMap                   3338       2743  -17.83%
Vom___Encode_____VMap                   6516       7004   +7.49%
Vom___EncodeMany_VMap                    719        527  -26.70%
Vom___Encode_____XSmallStruct          11613      10277  -11.50%
Vom___EncodeMany_XSmallStruct           2094       1630  -22.16%
Vom___Encode_____VSmallStruct           9404       8227  -12.52%
Vom___EncodeMany_VSmallStruct            470        405  -13.83%
Vom___Encode_____XLargeStruct          63816      51583  -19.17%
Vom___EncodeMany_XLargeStruct          37361      28332  -24.17%
Vom___Encode_____VLargeStruct          37287      32095  -13.92%
Vom___EncodeMany_VLargeStruct          11101       9271  -16.49%
Vom___Encode_____XLargeStructZero      43606      40770   -6.50%
Vom___EncodeMany_XLargeStructZero      19131      17869   -6.60%
Vom___Encode_____VLargeStructZero      23724      23690   -0.14%
Vom___EncodeMany_VLargeStructZero        224        217   -3.12%
Vom___Encode_____VSmallUnion            9225       7531  -18.36%
Vom___EncodeMany_VSmallUnion             266        227  -14.66%
Vom___Encode_____Time                   9340      10026   +7.34%
Vom___EncodeMany_Time                   1794       1732   -3.46%
Vom___Encode_____Blessings             41833      38908   -6.99%
Vom___EncodeMany_Blessings              5743       5291   -7.87%
Vom___Encode_____RPCRequestZero        50452      47035   -6.77%
Vom___EncodeMany_RPCRequestZero          217        208   -4.15%
Vom___Encode_____RPCRequestFull        63186      56772  -10.15%
Vom___EncodeMany_RPCRequestFull         5448       4803  -11.84%
Vom___Encode_____RPCResponseZero       51641      45973  -10.98%
Vom___EncodeMany_RPCResponseZero         809        831   +2.72%
Vom___Encode_____RPCResponseFull      101767     105144   +3.32%
Vom___EncodeMany_RPCResponseFull       57135      55317   -3.18%

MultiPart: 4/5
Change-Id: I60849a228d09b891854cd50fa96eb8b54b8198a2
1 file changed
tree: c5562ae8d672bdd4218cc4c29faa91ffcf488a12
  1. go/
  2. .gitignore
  3. AUTHORS
  4. CONTRIBUTING.md
  5. CONTRIBUTORS
  6. LICENSE
  7. PATENTS
  8. README.md
  9. VERSION
README.md

Physical lock

This project defines the software for building a secure physical lock using the Vanadium stack, along with a commmand-line tool for interacting with the lock. The software runs on a Raspberry Pi and interacts with the locks's switches and sensors using GPIO. It runs a Vanadium RPC service that allows clients to send lock and unlock requests.

Before describing the design in detail we highlight its key distinguishing aspects:

  • Decentralized: There is no single authority on all the locks, no cloud server that controls access to all locks from a particular manufacturer. All secrets and credentials pertaining to a particular lock are solely held by the lock and its clients. Huge compute farms run by hackers all over the world have no single point of attack that can compromise the security of multiple locks.

  • No Internet Connectivity Required: The lock does not require internet connectivity to function. When you’re right in front of the device, you can communicate with it directly without going through the cloud or a third-party service.

  • Audited: The lock can keep track of who opened the door, when and how they got access.

Design

An out-of-box lock device exposes the UnclaimedLock interface on startup.

type UnclaimedLock interface {
     Claim(name string) (security.WireBlessings | error)
}

This interface has a single method Claim that can be used to claim the device with a specific name and obtain credentials that grant authorization for subsequently interacting with the lock. Once claimed, the device exposes the Lock interface.

type Lock interface {
     // Lock locks the lock.
     Lock() error
     // Unlock unlocks the lock.
     Unlock() error
     // Status returns the current status (locked or unlocked) of the
     // lock.
     Status() (LockStatus | error)
}

Clients that possess the appropriate authorization credentials can interact with the device and send it requests to lock, unlock or determine the status.

Security Model

An out-of-box lock device comes with pre-installed credentials (private key and blessings) from its manufacturer. For instance, it may have blessings of the form <lock manufacturer>:<serial no>. The UnclaimedLock interface exposed by it is accessible to everyone.

When the lock is claimed with a specific name, it blesses itseld under the name and uses that blessing to subsequently authenticate to clients. It also creates a blessing for the claimer by extending this (self) blessing with the extension key. For e.g., a lock claimed with the name front-door will subsequently authenticate with the blessing front-door, and grant the blessing front-door:key to the claimer.

The blessing granted to the claimer is the key to the lock. It provides access to the methods on the Lock interface exposed by a claimed lock device. Furthermore, all extensions of this key blessing are also authorized to access the Lock interface.

Access to the lock can be shared with other principals by blessing them with the key blessing. Appropriate caveats may be added to this blessing to limit the scope of use. For e.g., the key front-door:key can be shared with a house guest for a limited period by extending it as front-door:key:houseguest with an expiration caveat.

Device Discovery

Both claimed and unclaimed lock devices advertise their endpoints using MDNS to allow clients to discover them without going through the cloud or a third-party Mounttable. All lock devices advertise themselves with names that are prefixed with lock-. This prefix allows clients to easily filter MDNS advertisements for lock devices.

An unclaimed lock device advertises under a name of the form lock-unclaimed-lock-xxxxxx where xxxxxx is a random number generated by the lock device. A claimed lock device advertises itself under the name lock-<claimed name>.

Currently we don't have any protection for preventing a rogue device from impersonating the MDNS name of a lock device. This is something we plan to add in the future. Note that impersonating the MDNS name of a lock device may lead to denial of service but does not compromise the security of the lock.

Locking and Unlocking

The Lock interface has methods to lock, unlock and determine the status of the lock device. The device logs the blessings of the caller for all method requests thereby maintaining an audit trail. Internally, these methods set or unset certain GPIO pins on the Raspberry Pi based on the circuitory described below.

Equipment

  • 10KΩ resistor
  • 1KΩ resistor
  • Magnetic switch (normally open circuit - closed when the sensor moves away) (e.g. P/N 8601 Magnetic Switch)
  • For now, an active buzzer to simulate a relay. Will fill in the relay details here once we have that setup.
  • Breadboard, jumper cables, ribbon cable - or whatever is needed to connect to the RPi's GPIO pins

Circuitry

Apologies for this unconventional, possibly unreadable circuit representation. Putting it down so that the author can remember! TODO(ashankar): Clean it up!

The pin number assignments here work both for RaspberryPi Model B/B+ and RaspberryPi2-ModelB.

---(Pin 1)-------/\/\(10KΩ)/\/\---------(COM port of magnetic switch)
                                  \
                                   \----/\/\(1KΩ)/\/\---------(Pin 15 = GPIO22)
                                                          \
                                                           \----(LED)-----|
                                                                          |
                                                                          |
                                          (N.O. port of magnetic switch)--|
                                                                          |
                                                                          |
                                         (-ve terminal of active buzzer)--|
                                                                          |
                                                                          |
                                                                          |
                                                           (Pin 6 = GND)--|

---(Pin 11 = GPIO17)-----------(+ terminal of active buzzer)

The above ciruit is meant to be a simulation of an actual lock device wherein locking and unlocking simply makes a buzzer ring. In particular the Lock and Unlock calls update the status of the pin GPIO17, and the Status call checks the status of the ping GPIO22.

Deployment

Building the lock service for the RaspberryPi

jiri go get -u github.com/davecheney/gpio
JIRI_PROFILE=arm jiri go install v.io/x/lock/lockd
scp $JIRI_ROOT/release/projects/physical-lock/go/bin/lockd <rpi_scp_location>

If building without the arm profile, there are no physical switches/relays and instead a simulated hardware is used that uses the interrupt signal (SIGINT) to simulate locking/unlocking externally.

The lock service can be started by running the following command in the directory where the lockd binary was copied.

lockd --v23.credentials=<creds dir> --config-dir=<config dir>

The --config-dir flag specifies a path to a directory where configuration files can be saved, and the --v23.credentials flag specifies a path to the Vanadium credentials directory for lockd. The configuration directory must be persisted accross restarts; emptying it would amount to a “factory reset”.

Sample Usage

We describe a few commands for discovering and interacting with a lock device.

The first step is to build the command-line tool.

jiri go install v.io/x/lock/lock

In the rest of this section, we specify all commands relative to the directory containing the lock tool.

All commands must be run under an agent that has a blessing from dev.v.io. This can be achieved by starting bash under an agent and using the principal tool to obtain a blessing from dev.v.io.

jiri go install v.io/x/ref/cmd/principal
jiri go install v.io/x/ref/services/agent/...

$JIRI_ROOT/release/go/bin/agent --v23.credentials=<creds> bash
$JIRI_ROOT/release/go/bin/principal seekblessings

Above, <creds> points to a directory where the tool's Vanadium credentials would be persisted.

Scanning for lock devices

Find the names of nearby lock devices (on the same network as this tool)

lock scan

This command prints out the names of nearby lock devices. The name of an unclaimed lock device always begins with unclaimed-, and the name of a claimed lock device is the name under which it was claimed.

Claiming an unclaimed device

The claim command can be used to claim an unclaimed device. For instance, the following command claims the device unclaimed-lock-xxxxxx with the name front-door. (Here unclaimed-lock-xxxxxx is the name of the unclaimed lock device obtained from the scan command.)

lock claim unclaimed-lock-xxxxxx front-door

The lock would now authenticate with the name front-door and a subsequent invocation of scan should print the name front-door.

Listing available keys

The listkeys command lists the set of available physical-lock keys and the names of the locks to which they apply.

lock listkeys

For example, as a result of the previous claim, the key front-door:key would show up as being available to this tool for the lock front-door.

Locking and Unlocking

The lock front-door can be locked using

lock lock front-door

and unlocked using

lock unlock front-door

The status command can be used to determine the current status of the lock.

lock status front-door

Sharing keys

The lock tool can also be used to share keys with nearby users (on the same network as this tool). This involves the following steps.

First, the receiver must run the recvkeys command on their lock tool.

// At the receiver
lock recvkeys

As a result of this command, the receiver's lock tool would start a server for receiving keys and advertise the endpoint of the server over MDNS.

Next, the sender must then search its neighborhood for users waiting to receiving keys

// At the sender
lock users

This command would print the email addresses of all users waiting to receive keys. Once the email address of the receiver is visible then this command can be stopped and sendkey command can be used to send a key.

For instance, the following command sends the key for lock front-door to the user john.smith@gmail.com that is only valid for 10 minutes.

// At the sender
lock sendkey --for=10m front-door john.smith@gmail.com friend

Above, friend is the category used to classify the receiver.

The sendkey command would cause the receiver to see a prompt specifying the name of the key received and the blessings of the sender. Once the receiver confirms saving the key, he/she would be able to use it to interact with the lock front-door for next 10 minutes. Executing the listkeys command would reveal the key front-door:key:friend along with its expiration time.

Future Work

  1. Auditing: Make lock service persist all requests received by it in an audit log. This may be a struct of the form:
AuditLog(startTime, endTime time.Time) []AuditLog | error
type AuditLog struct {
  Blessing string
  Action LockStatus
  Timestamp time.Time
}

We'd also have to work out how to control access to this AuditLog. One option is to use caveats - so when “making” a new key one can choose to insert the “noadmin” caveat?

  1. Caveats: Currently the sendkey command only supports expiration commands. In the future, we would like to support at least the following caveats:
  • Time-Range Caveat: This caveat would restrict the key so that it can only be used within a specific time range.
  • Ask-Permission Caveat: This is a third-party caveat that requires the holder of the key to ask the granter for permission before using a key.