Abstracting crypto into "builtin" ocap abstractions


#1

CryptoBrandMaker

Back in the old e-land days (1999-2001) we discussed the cryptoBrandMaker. Some thread roots including some discussion with Hal Finney (!!):

https://web.archive.org/web/20160418232953/http://www.eros-os.org/pipermail/e-lang/1999-November/003019.html
https://web.archive.org/web/20160616101200/http://www.eros-os.org:80/pipermail/e-lang/1999-November/003020.html
https://web.archive.org/web/20160418232841/http://www.eros-os.org/pipermail/e-lang/1999-November/003045.html
https://web.archive.org/web/20160615073842/http://www.eros-os.org:80/pipermail/e-lang/1999-November/003049.html
https://web.archive.org/web/20160616101701/http://www.eros-os.org:80/pipermail/e-lang/2001-October/005870.html

Both the old CapTP (as of those discussion) and the new CapTP explained at https://www.youtube.com/watch?v=YXUqfgdDbr8&list=PLKr-mvz8uvUgybLg53lgXSeLOp4BiwvB2 both start by providing cryptographic support only for a minimal model of distributed ocaps: passing messages to caps carrying caps. Back then we also had an approach to bring in support for other fancy crypto, by packaging it into object abstractions, as if it had been implemented by simple ocap code. Writing such ocap code for the semantics of the cryptoBrandMaker in modern SES:

export function makeBrandPair() {

  const amp = new WeakMap();

  return harden({
    seal(payload) {
      if (typeof payload !== 'string') {
        throw new Error('Only strings can be sealed');
      }
      const box = Object.create(null);
      amp.set(box, payload);
      return box;
    },
    unseal(box) {
      if (!(amp.has(box))) {
        throw new Error('not sealed by my sealer');
      }
      return amp.get(box);
    }
  });
}

For those sharing a mutually trusted platform, this provides an analog of public key encryption/decryption pairs. However, for this naive implementation, when Alice in VatA passes sealer, unsealer, or box over the network to Bob in VatB, what Bob receives is a remote reference back to the corresponding objects on VatA. These remote references obviously cannot pass any VatB-local immutability test, since these remote references cause network messages.

Given that CapTP is in bed with a cryptographic implementation of the same semantics and API using public key, these objects can be pass-by-copy and can pass Bob’s immutability test that is local to VatB. This explains how the special security properties of public key cryptography as abstracted into this API manifests as different behavior that could not have been achieved with CapTP by itself.

The needed cryptographic bits would be encapsulated in their respective cryptoBrandMaker objects, and transmitted between machines to transmit these objects. But these bits would not be exposed to other objects through their object API. This is consistent with the encapsulation of the cryptographic secrets of base CapTP as explained at http://www.erights.org/elib/capability/dist-confine.html

Where to put the secrets?

Back in those days, the anticipated implementation is that these would be passByConstruction, and the cryptographic bits would actually be inside the objects. However, for replicated platforms, like blockchain vats or quorum vats, the bits are revealed to all participants in the replication. For a public chain, the bits would be publicly visible. In this case, the unsealer should not be sent to a public chain because it would reveal the decryption key. The box would be fine, as it reveals only the cryphertext. The sealer would only reveal the encryption key, but would be useless on the public chain because any plaintext argument would also be publicly visible.

For the corresponding signing-key/verification-key pair, both the (open) box (revealing the signed plaintext) and the verification key would be perfectly happy on chain, providing only integrity not confidentiality.

Recently, another example came up, prompting this post.

Hash Commitment

The code for a hash commitment as a symbolic ocap program is amusingly similar to the code for the brand maker above.

export function makeCommitmentMaker() {

  const amp = new WeakMap();

  return harden({
    makeCommitment(payload) {
      if (typeof payload !== 'string') {
        throw new Error('Only strings can be committed');
      }
      const commitment = Object.crteate(null);
      amp.set(commitment, payload);
      return commitment;
    },
    compare(c1, c2) {
      if (!(amp.has(c1) && amp.has(c2))) {
        throw new Error('not a commitment');
      }
      return amp.get(c1) === amp.get(c2);
    }
  });
}

#2

The crypto implementation of a commitment object could carry the hash. The differential behavior is exactly analogous to the previous examples: The commitment objects would be pass-by-copy and would encapsulate the hash. Alice could make a commitment to a secret she knows, and send this commitment to Bob. The symbolic commitment would be a remote reference back to the real commitment on VatA. The crypto commitment would instead be pass-by-construction and pass an immutability test local to VatB.


#3

This is interesting to me for the specific application I’m intending for my use of Distributed Jessie. I would also like to suggest you have a look at Tink, if you haven’t already. I’ve been following it as a cross-platform API for crypto primitives.

[I won’t hijack this thread further, as I’m in the process of posting a question about zero-knowledge storage and ocaps.]