taxonomy and use cases

Folks:

Please read the following letter. The author, Brian Warner, has been
my partner for the last six years in designing and implementing
Tahoe-LAFS ¹ (which makes an appearance toward the end of this
letter), and he has written exactly what I wish I had written about
the general topic of crypto in Javascript. Reading this letter from
him helps me understand why I'm so little interested in the "wrapping
and identifying keys" features.

Of the three kinds of crypto-in-JS in Brian's taxonomy -- 1. fast
math, 2. primitives, 3. separation of authority on the web -- it
sounds like our WG is trying to standardize both 2 and 3. Kind 3 seems
to be inapplicable to most of my use cases, which is why I'm keen to
see kind 2 standardized well.

By the way, I disagree with Brian when he writes that it is not worth
designing a system which can run code from foo.com on user data while
preventing foo.com from accessing that data. I think that is currently
impossible, but will be possible in the future (by the addition of new
UI in the web browser that allows users to see and control which
versions of the code from foo.com get run).

Regards,

Zooko

¹ https://tahoe-lafs.org


=== Crypto-in-Browser Taxonomy
---   Brian Warner
---   warner@mozilla.com

There are several different runtimes I'm interested in targetting
(DOMCrypt itself might not address all of them, but it'd be nice to have
an API that's good for all three):

 * web content (the usual page environment)
 * addons / chrome code
 * Node.js

And I'd like to split out potential features into three separate
categories: this might make it easier to argue about the details:

 A: fast math for Javascript
 B: correct implementations of standard/common cryptographic algorithms
 C: separation of authority: content-vs-browser, origin-A-vs-origin-B

Category A (fast math) is about adding real integers, maybe bignums,
native-code constant-time modular-exponentiation functions, and proper
mutable bytes/UInt8Array types (with bit-shifts and XOR). This would
make it possible for crypto developers (i.e. the authors of Crypto++ or
Botan or SJCL) to implement any algorithm in JS without going through
the numeric contortions currently required (like using 52 bits of each
float, depending on the browser). This is clearly just about JS the
language, independent of where it's being run. I'd want to include a
better entropy source in this category, even if it's really coming from
a DOM-ish place like window.crypto, because it feels like a fundamental
tool that really needs to come from the platform (ala /dev/urandom).

Category B starts at the bottom of the stack with primitives for AES,
SHA256, RSA, etc. Higher up this stack are simpler (less footgun-prone)
"box/unbox" -style methods, like the ones exposed by the
"crypto_secretbox" functions in http://nacl.cr.yp.to/secretbox.html .
Again, these are just providing quality implementations of useful
algorithms for Javascript, and are independent of the runtime
environment. If we had fast math, we could probably build these out of
pure JS, but it might be faster to build them with native code, and
safer to provide a pre-packaged well-reviewed implementation than to
hope developers will find the right library.

Every function in this category is handed the raw key material that it
needs as an argument. Having low-level primitives would enable
crypto-savvy developers to build two things: new high-level tools, and
applications which are compatible with existing deployed protocols.
High-level tools are safer for less-savvy developers, but less flexible.

Even if we only had code for categories A and B, it'd be a big win. We'd
have tools to use quite safely in the addon/chrome/Node.js world. We'd
also have tools to let foo.com give me web-page code with which we could
encrypt data against observation by bar.com.

Category C (separation of authority) is where it gets fuzzy. This part
is about hiding the key material inside some box, and giving access to
"sign" or "decrypt" methods on the box but not revealing the real keys.
It's also about controlling invocation according to the origin of the
code doing the sign/decrypt call. It makes slightly more sense to do
this with the high-level tools (box/unbox) than with e.g. low-level AES,
so I think the discussion sometimes assumes that "high-level" means
"separated-authority", when really they're orthogonal.

Some people think that there's no point to writing any crypto code that
doesn't provide category C. I think this argument depends upon two
assumptions: "web content is the only interesting runtime environment",
and "encrypted data produced by code that came from foo.com will only
ever be sent back to foo.com". Both used to be true (before Node.js,
JS-based addons, or CORS/cross-domain-XHR), but are now false.

The purpose of third part, in my mind, is to introduce a form of
revocation: rather than having code generate raw key material directly,
it asks the implementation to create keys trapped inside some module
boundary and return to it a handle through which those keys can be
exercised. The handle (which could be represented by a special JS
object, or just an index number) is only useful in certain JS
environments (maybe only web-pages loaded from the same origin as the
code which created those keys). This code can't leak the raw keys (which
would be irrevocable), but it can sign/decrypt on behalf of other
parties (until it gets shut down).

In some cases, the human user will be involved in managing this
sign/decrypt authority: some of the proposals add UI, where the user is
asked to approve an action (initiated by web-page content and satisfied
by chrome-side code). They might also be asked to provide a secret (like
a master password, to unlock the other keys). The user needs to be given
enough information to make this an informed decision, so the usual
issues apply (having a trusted path to the browser chrome that can't be
spoofed by web content, having recognizable petnames to identify
origins, etc).

Introducing controls like this may improve safety on some fronts, but
also reduce flexibility on others, and presumes certain use cases more
than others (the first two categories are pretty use-case-agnostic).
Note that the first two parts are already implemented in various ways,
like SJCL, (even if they aren't as efficient or well-reviewed as we'd
prefer). It's the authority-separation stuff which requires changes to
the environment.


Finally, a note about the "use code from foo.com to encrypt data against
foo.com" case. There's a strong argument that this is pointless: foo.com
can easily (and mostly undetectably) serve you alternate code which
reveals your secrets to them. Keeping the actual key secret and exposing
an encrypt/decrypt method is marginally better: you can revoke access to
the key, but only after you discover that they're abusing it.

Code on a web page can change every time you hit that URL. Code in a
native application is still mutable, but it doesn't usually change quite
so frequently (most apps still have noisy user-visible update processes,
but we're slowly moving to the web model where updates occur silently
and without user approval). I think there's a spectrum here, from
slow-moving fundamental code (OS kernel, browser) to fast-moving
load-each-time maybe-cache-it code (web pages).

The important metric is: if the provider of the code wanted to make you
run a malicious version, how easily could they do it, and are they
likely to get caught? Imagine that all app upgrades were cross-checked
against a dozen of your friend's copies, and published in some
uncensorable way, before being applied. Now a app vendor who sneaks some
leak-your-key change into your copy of the code would probably be caught
when 1: your app warns you when it notices that it's installing an
upgrade that nobody else has seen, and 2: the world can decompile the
code you were asked to install and present evidence of the attack. If
the app vendor's reputation is important to them, they'll be
disincentivized to attempt this sort of attack.

When the code in question is open-source, we can pretend that some users
will examine the code for each upgrade, compile it themselves, then run
only their own trusted copy. Anyone who takes shortcuts (i.e. most
everyone) will be vulnerable to the same things described above.

The notion of simultaneously trusting a person enough to give their code
access to your secrets, while not trusting the person with the secrets
themselves, is a little bit crazy. For this to be rational, they must
have some strong incentives to always give you good code.

If you don't think you can distinguish between foo.com and the code they
gave your browser, is there any point to doing the crypto in the browser
and not on foo.com's servers? If the data being used lives on the
browser, then I think there are two performance wins: local crypto will
be faster (no roundtrips), and spending CPU cycles at the edges instead
of on the server means less competition (user incur their own CPU costs,
instead of contending with other users for server-side CPU time).

But I'm really interested in the use case where I work with foo.com (and
the code they serve me) to encrypt data in a way that protects me
against the actions of bar.com . Think of foo.com as a web-app provider
and bar.com as a bulk-storage service. If we can do that, then we can
develop other tools to help protect me against foo.com: we might include
content hashes in URLs (so my bookmark is enough to verify that I'm
getting the reviewed version of the webapp), or I might locally cache
the app for a long time, or the app might actually be served by a
locally-trusted agent.

Finally, the use-cases I'm interested in are things like:

* http://tahoe-lafs.org in a Firefox addon and/or Node.js module. For
 this, I need AES-CTR, SHA256, RSA signatures, and eventually ECDSA
 signatures. I don't need authority separation. If it were Tahoe-like
 and didn't have to be compatible with Tahoe itself, I'd be delighted
 to just have djb's NaCl.

* http://hg.wearpants.org/wherestheparty (web content). In this project,
 code from one domain needs to to verify a signature (e.g. ECDSA) on
 data coming from a different domain, using a pubkey embedded in data
 from the first. Signing is done offline. It might also want to decrypt
 resources coming from the second domain before displaying them.

* Code to perform key negotiation (OTR-style) and data encryption for
 real-time messaging (secure IM) over websockets or WebRTC. Initially
 it could be done in web-content (still vulnerable to the code
 provider), but could migrate to an addon. Needs DH (curve25519 would
 be great), AES (in some authenticated-encryption mode), or just some
 high-level box/unbox primitives.

* Code to do anonymous-remailer operations like Sphinx
 (http://eprint.iacr.org/2008/475) and the Pynchon Gate
 (http://www.freehaven.net/anonbib/cache/sassaman:wpes2005.pdf): mostly
 symmetric operations plus a few modular-exponentiations.

* Code to do PAKE, which usually needs mod-exp, DH key-agreement, and
 zero-knowledge proofs of x given g^x.

-- 
Regards,

Zooko Wilcox-O'Hearn

Founder, CEO, and Customer Support Rep -- Least Authority Enterprises

https://leastauthority.com

Received on Friday, 29 June 2012 09:55:36 UTC