Skip to content

Instantly share code, notes, and snippets.

@domenic
Last active August 29, 2015 14:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save domenic/bccb9a521a2a6b0e3568 to your computer and use it in GitHub Desktop.
Save domenic/bccb9a521a2a6b0e3568 to your computer and use it in GitHub Desktop.
Using normal JS objects in Web Crypto

Our goal is to eliminate the non-constructible KeyAlgorithm interface, and all its derived counterparts (e.g. RsaKeyAlgorithm). We can just use plain JavaScript objects instead, represented by their counterpart Algorithm dictionaries (e.g. RsaKeyParams).

As WebIDL is lacking in technology for manipulating JS objects, this will require a line or two of prose.

On Formalization

One problem with the existing spec that makes formalizing this difficult is that the spec sets readonly attributes on instances, which should be impossible. E.g. section 18.4.5, Generate Key, step 12 says "Set the algorithm attribute of publicKey to be algorithm": but the algorithm attribute is readonly, and so it cannot be set.

We can work around this, without having to make extensive disruptive changes to the spec, by granting the spec special powers to set readonly attributes. We need to specially call out the interaction of these special powers with normal attribute semantics, however. We'll see that in action below.

(The correct, more extensive fix is to describe the internal slots of the object, which are mutable, and describe the object's getters, i.e. readonly attributes, as returning the values of those internal slots. I'll include that as an appendix.)

General Changes

Remove section 11.

In section 12, change readonly attribute KeyAlgorithm algorithm to readonly attribute object algorithm.

In section 12.3, change the description of algorithm to "An object representing the key algorithm used to generate the key. After being set, which is only possible by specification text, it should return the set object for each getter invocation. In practice, this member will have the shape specified by one of the Algorithm dictionaries."

Changes for an Example: RsaKeyAlgorithm

Remove section 18.4.4.

In section 18.4.5, Generate Key:

  • Removes steps 6-9
  • Change step 12 to be "Set the algorithm attribute of publicKey to be normalizedAlgorithm."

In section 18.4.5, Import Key:

  • Change step 3 to be "Let algorithm be a new RsaKeyParams."

Apendix A: Using Internal Slots

To properly formalize the setting of readonly attributes, which is not actually possible in JavaScript semantics—nor in WebIDL, from what I can tell—you'd have to resort to prose again, as Boris points out.

In the definition of the Key interface, you would say something like: Key instances have the internal slots [[type]], [[extractable]], [[algorithm]], and [[usages]].

In the definition of each attribute, e.g. type, you would say: "returns the value of this instance's [[type]] internal slot."

In any spec algorithms which need to mutate the internally-held state of a Key instance, e.g. section 18.4.5 Generate Key step 12, you would say "Set the [[algorithm]] internal slot of publicKey to be normalizedAlgorithm."

Appendix B: Using Constructors

Most objects which expose a bunch of getters (readonly attributes) are meant to be "immutable," i.e. created with a given set of values, then return those values in perpetuity. This is usually done with a constructor.

To do this, modify the approach in Appendix A by defining a constructor for Key, e.g. new Key(type, extractable, algorithm, usages). It should be fairly straightforward, e.g. it contains four simple steps of the form "Set this instance's [[type]] internal slot to the value of the type argument." WebIDL's overload resolution algorithm will ensure the correct types end up in the internal slots, so there are no worries there.

Once this is done, then instead of creating initially-invalid Key instances and mutating their internal state until they become valid, as in section 18.4.5 Generate Key, you could replace that whole sequence, from steps 10 through 14, with:

  • Let usages be the usage intersection of usages and ["encrypt", "wrapKey"].
  • Let _privateKey be a new Key object created as if by new Key("public", true, normalizedAlgorithm, usages).

Similarly steps 15 through 19 would be subsumed, and of course KeyPair could be done the same way. (Although, to be honest, KeyPair looks like a dictionary to me.)

@sleevi
Copy link

sleevi commented May 1, 2014

Regarding Appendix B:

Your proposal does nothing to solve the "initially-invalid Key instance and mutating internal state until they become valid".

There is a hidden property here - that is never exposed to JS (nor can it be) - at best called keyHandle - that represents a discrete resource managed by the user agent. This is no different than the File API.

In the File API, File has a constructor that allows it to be created from application-supplied data. However, the UAs do not use that constructor - because they create File objects with internal snapshot state that represent the file on disk.

With Keys, they have an internal handle that is an opaque committment to some key material being available via underlying storage. Exposing a constructor, that takes keying material, isn't possible - because the implementation needs to be asynchronous (for a variety of reasons). So we only have a single way to construct a Key object - with a handle that cannot be exposed to the Web Application.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment