Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Web Crypto API #3

Closed
marcoscaceres opened this issue Jul 1, 2013 · 29 comments

Comments

Projects
None yet
7 participants

@ghost ghost assigned wycats Jul 14, 2013

@ghost ghost assigned slightlyoff Oct 2, 2013

@domenic

This comment has been minimized.

Copy link
Member

commented Mar 28, 2014

I hear that this is getting urgent; multiple browsers are very close to shipping.

@domenic domenic added the urgent label Mar 28, 2014

@domenic

This comment has been minimized.

Copy link
Member

commented Mar 28, 2014

A few things I noticed on a 5-minute skim:

  • They use the weird WebIDL notReallyAnArray[] things.
  • "then return an error" is used in promise algorithms, after the algorithm has already returned. O_o. In general the promise verbiage should be updated.
  • They invent several new seemingly-redundant DOMException values.
  • Many classes, like SubtleCrypto and WorkerCrypto, do not have constructors.
@sleevi

This comment has been minimized.

Copy link

commented Mar 28, 2014

Boris Z. pointed out the notReallyAnArray[] thing, and they will likely be replaced with sequences<> ( per http://darobin.github.io/api-design-cookbook/#use-sequence-type )

Provide an example of your proposed verbage. The spec makes it clear that the Promise is rejected with a DOMException (eg: 14.3.11 - "If the following steps or referenced procedures say to return an error, execute resolver's reject(value) algorithm, with the returned error as the value argument and then terminate the algorithm. ")

What do you see as redundant for the exceptions? This was requested by multiple vendors. See http://lists.w3.org/Archives/Public/public-webcrypto/2014Mar/0035.html

What use case is there for SubtleCrypto and WorkerCrypto to having constructors? They are exposed directly on the Window/Worker global object. "New SubtleCrypto()" has seemingly no value for programming patterns.

@sleevi

This comment has been minimized.

Copy link

commented Mar 28, 2014

I would just add that the most fruitful way for feedback is "Here's the issue. Here's a proposal for a solution". There is no question that few in the WG have the necessary WHATWG/WebApps battle-tested spec experience, but we're eager to learn. Highlighting issues without solutions will not lead to any good resolutions :)

@domenic

This comment has been minimized.

Copy link
Member

commented Mar 28, 2014

Provide an example of your proposed verbage.

Please see https://github.com/w3ctag/promises-guide, especially e.g. https://github.com/w3ctag/promises-guide#delay-ms-

What do you see as redundant for the exceptions?

Well, if vendors really want them, I'm likely missing something, but I don't see what value DataError provides over TypeError (given how broad its definition is in JS), or UnknownError provides over Error.

What use case is there for SubtleCrypto and WorkerCrypto to having constructors?

The issue is not that they should be constructable, it's that they shouldn't be classes if they don't have constructors. See e.g. the two sections starting at https://github.com/w3ctag/spec-reviews/blob/master/2014/02/quota-management-api.md#use-dictionaries-instead-of-non-constructible-classes

I would just add that the most fruitful way for feedback is "Here's the issue. Here's a proposal for a solution".

Yes, of course! This issue tracker is just for an initial data-gathering pass before we assemble real feedback. The end result of such a document would be significantly more polished, along the lines of our previous reviews, as you can see by perusing this repo's contents.

@sleevi

This comment has been minimized.

Copy link

commented Apr 21, 2014

Any further updates?

@domenic

This comment has been minimized.

Copy link
Member

commented Apr 21, 2014

We can prioritize this during the current week, with a target of having something by next Monday. In the meantime, former TAG member @annevk has been doing a fantastic job filing issues, from what I have seen.

@domenic

This comment has been minimized.

Copy link
Member

commented Apr 28, 2014

Doing a full read-through now. Still in proto-notes mode; hoping to write up something better tomorrow.

  • Use cases is great; leading with it is excellent. Other specs should learn from this.
  • 4.3 starts adding disclaimers about "cryptographic providers and modules" but I don't even know what those are. A brief sentence explaining would be helpful to readers without the necessary domain knowledge.
  • 5.1 briefly mentions origin-based security. I imagine the normative text will expand on this but a bit more detail up front would be nice. Is that the spec's primary security model (like it is for most web specs)? Is there anything special about web crypto that we should consider in regard to origin-based security?
  • In general really liking section 5. Every paragraph of 5.2 is very good.
  • Section 6 has me scared. It proposes three problems, without giving any notion of a solution to any of them. Are we in trouble? Are implementers really OK with these?
  • Outdated reference to DOM4 Promises in 7.
  • WebIDL is one word, not two words.
  • Typed arrays are moving to ES6. It's worth checking that all uses of typed arrays are conformant with their new reality.
  • 9.2.1 TypeMismatchError is obsolete; use TypeError.
  • 9.2.1 use RangeError not QuotaExceededError.
  • As-is, the Algorithm dictionary seems useless, as it only contains a name value. (Just use a string in that case.) I see from the open issue that it might also contain params, but how does that show up? It seems like the WebIDL here is misleading. This object likely is not a WebIDL dictionary, if it can have varying values.
  • Why is KeyAlgorithm non-constructible? Why does it exist at all, instead of using a dictionary or just a string?
  • Why is Key non-constructible?
  • 12.4 monkey-patches structured clone, which is bad.
  • 14: generateKey looks like it's the missing constructor for Key. Maybe that's why key doesn't have a constructor: it must be generated asynchronously.
  • Definitely need to update promise verbiage, e.g. resolver objects do not exist, and the auto-rejection stuff has been incorporated into WebIDL. It is good that you guys took the trouble to note this explicitly and prevent any sync errors from being thrown. But yeah, as mentioned above, "return an error" is super-confusing.
  • The step-by-step nature of the method specification is very good. Not all specs do this.
  • As mentioned above the non-constructible classes should just be normal JS objects, since otherwise there's no explanation for how e.g. window.crypto exists if nobody is allowed to construct WindowCrypto instances.
  • BigInteger via Uint8Array is a hack. Maybe it's one that's necessary for now, but it really sucks. Could strings be used instead, perhaps? Or would that not be performant? I guess when ECMAScript grows big integers (which is pretty far future), then they can be added as an overload. Still it would be nice for the spec to note how this is a limitation of ECMAScript, and maybe comment on why strings are not good.
  • 18.1 gives a table without giving any context for why certain boxes are checked. Some explanation of e.g. "some algorithms are only appropriate for encryption and decryption, some for signing and verification" would help readers without the domain knowledge necessary to see immediately why this table is structured the way it is. Right now, I think it's equally plausible to interpret it as "here's what implementers have implemented" or similar.
  • Now that I'm getting into 18.4.3 I am starting to see the Algorithm vs. KeyAlgorithm dichotomy show up again. Again, the non-constructible KeyAlgorithm things seem bad. Why not just use "dictionaries" (i.e. plain JS objects) for those too?
  • The code examples look pretty nice. That is, in the end the API is pleasant to use. Yay!
  • You might be able to make the code examples nicer by using arrow functions, e.g. console.log.bind(console, "The signature is:") could become signature => console.log("The signature is:", signature).

Whew. Done. Will try to type up in a format that's less raw and contains "here's a proposal for a solution" tomorrow.

@sleevi

This comment has been minimized.

Copy link

commented Apr 28, 2014

On Sun, Apr 27, 2014 at 9:15 PM, Domenic Denicola
notifications@github.comwrote:

Doing a full read-through now. Still in proto-notes mode; hoping to write
up something better tomorrow.

  • Use cases is great; leading with it is excellent. Other specs should
    learn from this.
  • 4.3 starts adding disclaimers about "cryptographic providers and
    modules" but I don't even know what those are. A brief sentence explaining
    would be helpful to readers without the necessary domain knowledge.
  • 5.1 briefly mentions origin-based security. I imagine the normative
    text will expand on this but a bit more detail up front would be nice. Is
    that the spec's primary security model (like it is for most web specs)? Is
    there anything special about web crypto that we should consider in regard
    to origin-based security?
  • In general really liking section 5. Every paragraph of 5.2 is very
    good.
  • Section 6 has me scared. It proposes three problems, without giving
    any notion of a solution to any of them. Are we in trouble? Are
    implementers really OK with these?

This needs some degree of refinement, as it was largely drafted early in
the WG process when http://www.w3.org/TR/webcrypto-key-discovery/ was still
considered in scope.

Because the current API does not provide for any storage mechanisms (as
earlier drafts did), the new surface for tracking identifiers (again,
EXCLUDING named key discovery and future work the WG wants to take on, like
hardware tokens) is no different than IDB.

  • Outdated reference to DOM4 Promises in 7.
  • WebIDL is one word, not two words.
  • Typed arrays are moving to ES6. It's worth checking that all uses of
    typed arrays are conformant with their new reality.
  • 9.2.1 TypeMismatchError is obsolete; use TypeError.
  • 9.2.1 use RangeError not QuotaExceededError.
  • As-is, the Algorithm dictionary seems useless, as it only contains a
    name value. (Just use a string in that case.) I see from the open
    issue that it might also contain params, but how does that show up? It
    seems like the WebIDL here is misleading. This object likely is not a
    WebIDL dictionary, if it can have varying values.

I'm not sure I fully follow this.

You may find your question already answered on this thread -
http://lists.w3.org/Archives/Public/public-webcrypto/2014Mar/0099.html - in
particular the edits that are still pending (the draft in LC is not really
LC ready).

Consider the proposal from
http://lists.w3.org/Archives/Public/public-webcrypto/2014Mar/0140.html

  • Why is KeyAlgorithm non-constructible? Why does it exist at all,
    instead of using a dictionary or just a string?

It's an attribute on key objects. You cannot expose a dictionary type as
an attribute.

As you can see in the algorithm-specific sections, KeyAlgorithm is
specialized into appropriate sub-types (eg: RsaKeyAlgorithm), which expose
additional attributes.

  • Why is Key non-constructible?

I'm suggesting we have some conceptual mismatches here on the API, judging
by this question. Perhaps this was a draft you found answered earlier?

Creating a Key object (eg: from key material) is inherently an asynchronous
operation. The only means to bring key material in to the API is through
importKey, unwrapKey, or generateKey.

There is no such concept as a Key object without a Key.

Explain why. This is a new object type - with real platform resources -
that needs to support structured clone. How is this any different than the
File API, which described how File objects can be structured cloneable
(until it was eventually incorporated into the WHATWG draft)

Such monkey-patching is essential for any APIs that wish to expose new
objects/device capabilities, it would seem.

  • 14: generateKey looks like it's the missing constructor for Key.
    Maybe that's why key doesn't have a constructor: it must be generated
    asynchronously.

Correct

  • Definitely need to update promise verbiage, e.g. resolver objects do
    not exist, and the auto-rejection stuff has been incorporated into WebIDL.
    It is good that you guys took the trouble to note this explicitly and
    prevent any sync errors from being thrown. But yeah, as mentioned above,
    "return an error" is super-confusing.

Yes. That was raised.

  • The step-by-step nature of the method specification is very good.
    Not all specs do this.
  • As mentioned above the non-constructible classes should just be
    normal JS objects, since otherwise there's no explanation for how e.g.
    window.crypto exists if nobody is allowed to construct WindowCryptoinstances.

That doesn't seem to match what's documented on
http://heycam.github.io/webidl/ , so I would appreciate if your write-up
could actually quantify why this is.

  • BigInteger via Uint8Array is a hack. Maybe it's one that's necessary
    for now, but it really sucks. Could strings be used instead, perhaps? Or
    would that not be performant? I guess when ECMAScript grows big integers
    (which is pretty far future), then they can be added as an overload. Still
    it would be nice for the spec to note how this is a limitation of
    ECMAScript, and maybe comment on why strings are not good.

I'm curious what you mean by hack? It's purely a syntactic typedef that
describes the type of ArrayBuffer(View) that is expected. Is this any more
a hack than specifying how SPKI/PKCS8 return binary data that is ASN.1 DER
encoded (that is, it's binary data with a particular structure)

  • 18.1 gives a table without giving any context for why certain boxes
    are checked. Some explanation of e.g. "some algorithms are only appropriate
    for encryption and decryption, some for signing and verification" would
    help readers without the domain knowledge necessary to see immediately why
    this table is structured the way it is. Right now, I think it's equally
    plausible to interpret it as "here's what implementers have implemented" or
    similar.
  • Now that I'm getting into 18.4.3 I am starting to see the Algorithm
    vs. KeyAlgorithm dichotomy show up again. Again, the non-constructible
    KeyAlgorithm things seem bad. Why not just use "dictionaries" (i.e. plain
    JS objects) for those too?

Because that's not valid WebIDL, as Microsoft was clear to call out.

  • The code examples look pretty nice. That is, in the end the API is
    pleasant to use. Yay!
  • You might be able to make the code examples nicer by using arrow
    functions, e.g. console.log.bind(console, "The signature is:") could
    become signature => console.log("The signature is:", signature).

Whew. Done. Will try to type up in a format that's less raw and contains
"here's a proposal for a solution" tomorrow.


Reply to this email directly or view it on GitHubhttps://github.com//issues/3#issuecomment-41521737
.

@domenic

This comment has been minimized.

Copy link
Member

commented Apr 28, 2014

I'm not sure I fully follow this. You may find your question already answered on this thread - http://lists.w3.org/Archives/Public/public-webcrypto/2014Mar/0099.html - in particular the edits that are still pending (the draft in LC is not really LC ready).

Yes, the proposal there does seem to be heading in the right direction.

In general, for this and the other dictionary-vs.-object related issues, spec authors should be concerned less about what WebIDL restricts you to, and more about what is idiomatic and convenient for JavaScript authors. In particular, creating inheritance hierarchies of non-constructible types is very bad. (Especially since JavaScript inheritance relies very strongly on the existence of a working constructor!) The ideal situation does not involve such type hierarchies, but instead plain old JavaScript objects (which derive from Object.prototype, not NonConstructibleClass.prototype). Specs should do whatever necessary to work around the limitations of WebIDL in this regard, including e.g. invoking "convert to dictionary" algorithms manually.

It's an attribute on key objects. You cannot expose a dictionary type as an attribute.

Right, this is one of those WebIDL limitations I was mentioning. This came up in screen orientation as well, and our strong recommendation is to work around this limitation by returning object and manually invoking the necessary algorithms, in order to avoid creating such inheritance hierarchies.

I'm suggesting we have some conceptual mismatches here on the API, judging by this question. Perhaps this was a draft you found answered earlier? Creating a Key object (eg: from key material) is inherently an asynchronous operation. The only means to bring key material in to the API is through importKey, unwrapKey, or generateKey. There is no such concept as a Key object without a Key.

The issue is that if the API is non-constructible, then instances of it should not exist. It is impossible for a JavaScript programmer to conceptualize such objects existing, if they cannot be constructed. For example, once an implementation gathers the key type, the extractable boolean, the key algorithm, and the key usages, how is it supposed to actually create a Key instance that can be exposed to users, if Key cannot be constructed?

A constructor such as new Key(type, extractable, algorithm, usages) or new Key({ type, extractable, algorithm, usages }) might help solve this problem.

Explain why. This is a new object type - with real platform resources - that needs to support structured clone.

Then it should coordinate with the specification that includes structured clone! Tagging in @annevk to explain this in case his blog post didn't help. I feel that it was explained very well there.

That doesn't seem to match what's documented on http://heycam.github.io/webidl/ , so I would appreciate if your write-up could actually quantify why this is.

Because it's literally impossible in JavaScript. You are creating an API that could only exist if implemented in a layer above JavaScript, i.e. an API that JavaScript developers will never be able to see in the wild. This kind of "magic" semantics is what gives the web platform a bad name.

I'm curious what you mean by hack? It's purely a syntactic typedef that describes the type of ArrayBuffer(View) that is expected.

It's a hack because a big integer type should ideally be a feature of the language, and not using something made for a sequence of 8-bit integers. JavaScript doesn't provide this feature, so you work around it with a typed array, but that's a workaround (hack).

Because that's not valid WebIDL, as Microsoft was clear to call out.

That's why I said "(i.e. plain JS objects)"; clearly WebIDL is limited here.

@sleevi

This comment has been minimized.

Copy link

commented Apr 29, 2014

On Mon, Apr 28, 2014 at 4:07 PM, Domenic Denicola
notifications@github.comwrote:

I'm not sure I fully follow this. You may find your question already
answered on this thread -
http://lists.w3.org/Archives/Public/public-webcrypto/2014Mar/0099.html -
in particular the edits that are still pending (the draft in LC is not
really LC ready).

Yes, the proposal there does seem to be heading in the right direction.

In general, for this and the other dictionary-vs.-object related issues,
spec authors should be concerned less about what WebIDL restricts you to,
and more about what is idiomatic and convenient for JavaScript authors. In
particular, creating inheritance hierarchies of non-constructible types is
very bad. (Especially since JavaScript inheritance relies very strongly on
the existence of a working constructor!) The ideal situation does not
involve such type hierarchies, but instead plain old JavaScript objects
(which derive from Object.prototype, not NonConstructibleClass.prototype).
Specs should do whatever necessary to work around the limitations of WebIDL
in this regard, including e.g. invoking "convert to dictionary" algorithms
manually.

This is not helpful or constructive feedback for spec authors who are not
deeply immersed in the ES politics.

I can only say that, having read your feedback, I cannot take any changes
back to the WG that will satisfy your request. I can only hope you write-up
provides exact suggestions, since the issues you raise are not well
documented in the context of spec authors.

I have attempted to closely mirror a variety of Web specs that already
exist - taking care and consideration into the various maturities of the
specs within the WebApps WG. If you can provide a single concrete example
of a spec you believe is "doing it right" - while equally demonstrating a
care to the very real knowledge that "SubtleCrypto", as an interface, is
expected to be exposed by different objects and with the implications of
different semantics, that would be helpful.

What I mean by this, concretely, is consider the case of a smart card.
Rather than accessing SubtleCrypto via window.crypto.subtle, one might
access it on a (specific) smart card token via
window.crypto.getSmartcards().then(smartcards => return
smartcards[3].subtle.encrypt(...));

It's an attribute on key objects. You cannot expose a dictionary type as
an attribute.

Right, this is one of those WebIDL limitations I was mentioning. This came
up in screen orientation as well, and our strong recommendation is to work
around this limitation by returning object and manually invoking the
necessary algorithms, in order to avoid creating such inheritance
hierarchies.

This is equally not helpful feedback, and surprises me that the TAG is
advocating such an approach as "Disregard key technologies used to write
interoperable specifications".

I feel there is insufficient explanation or justification for why such
significant costs need to be imposed on specification authors, when from
the developer perspective, it's indistinguishable (save for the behaviour
of the interface object, which is precisely something that we are
attempting to mask re: NoInterfaceObject)

I'm suggesting we have some conceptual mismatches here on the API,
judging by this question. Perhaps this was a draft you found answered
earlier? Creating a Key object (eg: from key material) is inherently an
asynchronous operation. The only means to bring key material in to the API
is through importKey, unwrapKey, or generateKey. There is no such concept
as a Key object without a Key.

The issue is that if the API is non-constructible, then instances of it
should not exist. It is impossible for a JavaScript programmer to
conceptualize such objects existing, if they cannot be constructed.For
example, once an implementation gathers the key type, the extractable
boolean, the key algorithm, and the key usages, how is it supposed to
actually create a Key instance that can be exposed to users, if Keycannot be constructed?

A constructor such as new Key(type, extractable, algorithm, usages) or new
Key({ type, extractable, algorithm, usages }) might help solve this
problem.

I fail to see how that does not create more problems. An author who can do
that has created a Key object that is incomplete, undefined, and unusuable.
It creates a specification unfit for purpose.

A key, fundamental function of this API is that it exposes an opaque type.
At some level, you MUST have an object that CANNOT be exposed in
Javascript, because it is by definition opaque. You cannot create such
opaque types through any language construct - they are opaque.

Your proposal seems to create significantly more problems than it solves,
at least as I can understand.

Explain why. This is a new object type - with real platform resources -
that needs to support structured clone.

Then it should coordinate with the specification that includes structured
clone! Tagging in @annevk https://github.com/annevk to explain this in
case his blog post didn't help. I feel that it was explained very well
there.

The entire purpose of WebIDL and notions like "implements" or "exposed"
seem to exist to allow specifications to be as loosely coupled as possible.
Specifications that act as "single points of failure" / gating factors - as
your seemingly suggesting - cannot help but feel like a centralized
engineering perspective that groups like Web Apps and WHATWG have tried
very hard to avoid - and for good reason.

For example, there is no reason to obligate a user implementing the HTML
specification to implement WebCrypto.

That doesn't seem to match what's documented on
http://heycam.github.io/webidl/ , so I would appreciate if your write-up
could actually quantify why this is.

Because it's literally impossible in JavaScript. You are creating an API
that could only exist if implemented in a layer above JavaScript, i.e. an
API that JavaScript developers will never be able to see in the wild. This
kind of "magic" semantics is what gives the web platform a bad name.

Um, the key point of this API - and why the WG was founded - is that this
IS impossible to implement in Javascript.

I'm curious what you mean by hack? It's purely a syntactic typedef that
describes the type of ArrayBuffer(View) that is expected.

It's a hack because a big integer type should ideally be a feature of the
language, and not using something made for a sequence of 8-bit integers.
JavaScript doesn't provide this feature, so you work around it with a typed
array, but that's a workaround (hack).

I fear we're talking past each other still, which does not inspire great
hope in the feedback. I can only say that, as a user of cryptographic APIs,
I have to respectfully disagree with you here. While there is value in
having notions such as Big Integers as "first class" citizens of an API,
they do not do cryptographic users or implementers any favours - they
inherently lead to insecure code, because manipulation of big integers -
and the cryptographic safety of it - is entirely context and algorithm
dependent.

A Montgomery reduction can be entirely safe in one algorithm, and
fundamentally insecure in another. Omitting leading zeroes can be perfectly
safe for ECDSA, but inherently disastrous for something like RSA. The
reason that few standard "big integer" libraries exist is because they are,
at best, a growing collection of hundreds of methods that are
context-specific - concepts very much anti-thetical to ideas of type.

The specification of BigInteger here, as a particularly structured
UInt8Array, is in line with the cryptographic specifications. They are
meant to be "octet strings" - sequences of bytes - and NOT generic numbers
that you perform arbitrary manipulation on - because the moment you do,
you're doing the exact thing Web Crypto was invented to PREVENT you from
having to do.

Because that's not valid WebIDL, as Microsoft was clear to call out.

That's why I said "(i.e. plain JS objects)"; clearly WebIDL is limited
here.

This seems to do readers/users of the API and implementors a great
disservice, by preventing the API from being readable/obvious, and instead
encumbered in layer upon layer of prose, to see exactly how that "object"
appears to an actual developer.

I am surprised and dismayed to see such advice.


Reply to this email directly or view it on GitHubhttps://github.com//issues/3#issuecomment-41625306
.

@sleevi

This comment has been minimized.

Copy link

commented Apr 29, 2014

On Mon, Apr 28, 2014 at 4:07 PM, Domenic Denicola
notifications@github.comwrote:

I'm not sure I fully follow this. You may find your question already
answered on this thread -
http://lists.w3.org/Archives/Public/public-webcrypto/2014Mar/0099.html -
in particular the edits that are still pending (the draft in LC is not
really LC ready).

Yes, the proposal there does seem to be heading in the right direction.

In general, for this and the other dictionary-vs.-object related issues,
spec authors should be concerned less about what WebIDL restricts you to,
and more about what is idiomatic and convenient for JavaScript authors. In
particular, creating inheritance hierarchies of non-constructible types is
very bad. (Especially since JavaScript inheritance relies very strongly on
the existence of a working constructor!) The ideal situation does not
involve such type hierarchies, but instead plain old JavaScript objects
(which derive from Object.prototype, not NonConstructibleClass.prototype).
Specs should do whatever necessary to work around the limitations of WebIDL
in this regard, including e.g. invoking "convert to dictionary" algorithms
manually.

It's an attribute on key objects. You cannot expose a dictionary type as
an attribute.

Right, this is one of those WebIDL limitations I was mentioning. This came
up in screen orientation as well, and our strong recommendation is to work
around this limitation by returning object and manually invoking the
necessary algorithms, in order to avoid creating such inheritance
hierarchies.

I'm suggesting we have some conceptual mismatches here on the API, judging
by this question. Perhaps this was a draft you found answered earlier?
Creating a Key object (eg: from key material) is inherently an asynchronous
operation. The only means to bring key material in to the API is through
importKey, unwrapKey, or generateKey. There is no such concept as a Key
object without a Key.

The issue is that if the API is non-constructible, then instances of it
should not exist. It is impossible for a JavaScript programmer to
conceptualize such objects existing, if they cannot be constructed. For
example, once an implementation gathers the key type, the extractable
boolean, the key algorithm, and the key usages, how is it supposed to
actually create a Key instance that can be exposed to users, if Keycannot be constructed?

A constructor such as new Key(type, extractable, algorithm, usages) or new
Key({ type, extractable, algorithm, usages }) might help solve this
problem.

Explain why. This is a new object type - with real platform resources -
that needs to support structured clone.

Then it should coordinate with the specification that includes structured
clone! Tagging in @annevk https://github.com/annevk to explain this in
case his blog post didn't help. I feel that it was explained very well
there.

Note that this is not a Monkey Patch - it's specifically following the
manner that @annevk suggested is a good thing (in the context of adopting
DOM nodes).

Namely, the structured clone specification states (in
http://www.whatwg.org/specs/web-apps/current-work/multipage/common-dom-interfaces.html#safe-passing-of-structured-data)

"If input is an object that another specification defines how to clone

Let output be a clone of the object as defined by the other specification."

That doesn't seem to match what's documented on
http://heycam.github.io/webidl/ , so I would appreciate if your write-up
could actually quantify why this is.

Because it's literally impossible in JavaScript. You are creating an API
that could only exist if implemented in a layer above JavaScript, i.e. an
API that JavaScript developers will never be able to see in the wild. This
kind of "magic" semantics is what gives the web platform a bad name.

I'm curious what you mean by hack? It's purely a syntactic typedef that
describes the type of ArrayBuffer(View) that is expected.

It's a hack because a big integer type should ideally be a feature of the
language, and not using something made for a sequence of 8-bit integers.
JavaScript doesn't provide this feature, so you work around it with a typed
array, but that's a workaround (hack).

Because that's not valid WebIDL, as Microsoft was clear to call out.

That's why I said "(i.e. plain JS objects)"; clearly WebIDL is limited
here.


Reply to this email directly or view it on GitHubhttps://github.com//issues/3#issuecomment-41625306
.

@domenic

This comment has been minimized.

Copy link
Member

commented Apr 29, 2014

I am disappointed that you cannot see the value in this feedback as-is, and will do my best to present it in a form (with concrete solutions) that can be useful to your group, when I perform the full writeup representing a TAG opinion.

One thing you must understand is that WebIDL is an old technology, with many problems and limitations that come from its legacy, and it does not represent how JavaScript-exposed APIs should be written in the modern day. The problems with it are extensive and well-known, albeit only to a small group (including the TAG). The purpose of exchanges such as this are to bring such problems to the fore, and present better solutions and workarounds. This is all a stopgap for some vendor actually investing time and human capital in improving WebIDL to allow it to represent modern JavaScript APIs. But in the meantime, we must work around the tools we are given, and not let its mistakes guide us down bad paths, e.g. by emulating existing APIs and conventions there.

I hope you can take this in the spirit it is given: I am not saying the API is inherently bad, or anything of that sort. I am saying that, as a consequence of the current human capital allocations of the web standards sphere, our spec-writing tools are suboptimal, and in the meantime the burden is indeed on the spec authors to work around those. In turn, it is on us (the TAG) to give concrete guidance as to what those workarounds should be, since as you note not all spec authors can be familiar with how JavaScript works. That is still forthcoming---as I've tried to communicate multiple times in this thread, this is just preliminary feedback notes that happen to be public, instead of kept on my local computer.

On specific points:

while equally demonstrating a care to the very real knowledge that "SubtleCrypto", as an interface, is expected to be exposed by different objects and with the implications of different semantics, that would be helpful.

This is very helpful, and does indeed indicate that SubtleCrypto may be a class (WebIDL "interface") instead of a simple JavaScript object (WebIDL "dictionary"). However, one must still have some way for that class to be constructed, since otherwise instances of it simply cannot exist in JavaScript.

It seems like currently you can create one for a window; one for a worker; and you are proposing that you could create one for a smart card. These seem like three potential constructor overloads to me.

On the other hand, I still can't quite understand what about the SubtleCrypto instances makes them stateful. Remember that a class should encapsulate both data and behavior; if there is no data, just behavior, then you are really looking at a module, which in JS is represented as a plain old object. Perhaps the data encapsulated is which computational resources are being utilized? I'd appreciate your help understanding this point, as in general making it explicit which data is encapsulated by a class is crucial to defining it and allowing it to be constructed.

when from the developer perspective, it's indistinguishable (save for the behaviour of the interface object, which is precisely something that we are attempting to mask re: NoInterfaceObject)

There are significant differences between a class and a plain old JavaScript object, and statements like this are very worrying. For example, the difference between accessor properties and data properties, or their behavior under introspection and shallow cloning operations, are serious usability problems presented by trying to shoehorn one into the other.

Again, we feel very strongly that non-constructible classes should not be used as substitutes for JavaScript objects, despite WebIDL lacking good technology to assist in these cases. We will strive to provide very detailed concrete guidance on how to fix this. Indeed, if your spec were on GitHub, I'd probably be doing a pull request for it to illustrate that.

I fail to see how that does not create more problems. An author who can do that has created a Key object that is incomplete, undefined, and unusuable. It creates a specification unfit for purpose. A key, fundamental function of this API is that it exposes an opaque type. At some level, you MUST have an object that CANNOT be exposed in Javascript, because it is by definition opaque. You cannot create such opaque types through any language construct - they are opaque.

Um, the key point of this API - and why the WG was founded - is that this IS impossible to implement in Javascript.

We must indeed be talking past each other, since nothing in this API seems to be beyond the grasp of a Turing complete language like JavaScript, and so it can definitely be implemented there. (The access to hardware randomness capabilities seems to be the only fundamentally new feature introduced.)

If C++ is able to create these opaque types, why not JavaScript? Once they are created in C++, there must be algorithm steps saying "initialize the object with this external state and this internal state." To do so, there should be a constructor, otherwise instances of the object cannot exist in JavaScript.

While there is value in having notions such as Big Integers as "first class" citizens of an API, they do not do cryptographic users or implementers any favours - they inherently lead to insecure code, because manipulation of big integers - and the cryptographic safety of it - is entirely context and algorithm dependent.

They are meant to be "octet strings" - sequences of bytes - and NOT generic numbers that you perform arbitrary manipulation on - because the moment you do, you're doing the exact thing Web Crypto was invented to PREVENT you from having to do.

I appreciate you being willing to explain this, and think I have come around to your perspective on big integers in this matter---especially saying that it is just one of many data encodings. I think the typedef may be misleading in this regard, since one sees "BigInteger" and one thinks "ugh, that should be a language feature, not a typedef." Perhaps a simple clarifying note, or renaming to e.g. ByteSequence, would help avoid this reaction.

Note that this is not a Monkey Patch - it's specifically following the manner that @annevk suggested is a good thing (in the context of adopting DOM nodes).

You are completely right; my bad. Thanks for pushing back on this.


I hope you can appreciate, by the fact that I did eventually come around to several of these points, that we are having a constructive dialog here :). Clearly there's still the issue of WebIDL's limitations, but I hope I have explained the problem more clearly in this last message.

I know your most recent message had a rather discouraged tone, but I personally am feeling better: it seems like this exchange is definitely shaping the spec feedback document I am writing up (and that you can take to the working group) into something that will focus on the core issues, without wasting time on points that end up being misguided (like my monkey patching misstep), or overblown (like the big integer question).

@domenic

This comment has been minimized.

Copy link
Member

commented Apr 30, 2014

@sleevi I put together a writeup what I think is the most important point, which is a concrete explanation of the changes necessary to eliminate the parallel KeyAlgorithm inheritance hierarchy. I hope it can be a helpful set of explicit changes and simplifications to the spec that you'd find useful:

https://gist.github.com/domenic/bccb9a521a2a6b0e3568

Let me know if this is heading in the right direction.

@sleevi

This comment has been minimized.

Copy link

commented May 1, 2014

I still don't see a solution for how to deal with the fact that Key
represents a distinct resource that is managed by a user agent, nor do I
comprehend the insistence that "objects must have constructors" - if
Platform Objects (as Keys truly are and need to be) are not creatable by
JS, that's a Good Thing (tm).

That is, a Key represents some set of public, read-only attributes - and
some set of internal resources.

I dislike the notion of "new Key(algorithm, type, extractable, usages)",
because that means everything that returns a Key is effectively:

  1. Let key be a new Key object as if created by new Key(foo, bar, baz, bat)
  2. Set the internal property [[handle]] of key to (some magic value here)

And it means that everything that wishes to use a Key object now has to, in
prose, describe something like:

  1. If the internal property [[handle]] of key is not (some magic value
    here), throw a Foo error

Rather than simply allowing the IDL to describe the fact that a Key object
can never be constructed by JS - it can only be constructed by 'that thing
outside the JS' - and let WebIDL handle checking that the arg instanceof
Key is true, which by definition means that the internal property
[[handle]] is true.

That said, I can appreciate the concerns about how properties are handled.
The existing WebApps specs are wildly inconsistent about this. The only
spec that I can see that readily matches your "preferred" solution seems to
be XHR 2, which uses the term "settings object" (
https://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html#concept-xmlhttprequest-settings-object/
http://www.w3.org/html/wg/drafts/html/master/webappapis.html#relevant-settings-object-for-a-global-object),
which seems to be used not only for the associated security checks
needed for the Fetch algorithm, but also for stashing internal variables
(eg: as described by Step 14 of
https://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html#the-open()-method ),
which just uses prose like

"Set the variables associated with the object as follows ..."

and then in the prose again, describes how this is handled (
https://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html#dom-xmlhttprequest-responsetype)

That is, the one spec that seems to do what you want, does it differently
than how you want.

More importantly, I suspect I may be missing something. Your objections
seem to be of the nature that "This isn't valid in JS (or WebIDL),
therefore you have to rely on the user agent doing something special" - but
how is this not equally true for the internal property slots ([[foo]]),
which at least according to http://es5.github.io/#x8.6 are merely syntactic
sugar for "The JS engine does something special here, but this is really
just semantic and syntactic sugar for 'here be dragons'"

On Tue, Apr 29, 2014 at 8:43 PM, Domenic Denicola
notifications@github.comwrote:

@sleevi https://github.com/sleevi I put together a writeup what I think
is the most important point, which is a concrete explanation of the changes
necessary to eliminate the parallel KeyAlgorithm inheritance hierarchy. I
hope it can be a helpful set of explicit changes and simplifications to the
spec that you'd find useful:

https://gist.github.com/domenic/bccb9a521a2a6b0e3568

Let me know if this is heading in the right direction.


Reply to this email directly or view it on GitHubhttps://github.com//issues/3#issuecomment-41757833
.

@domenic

This comment has been minimized.

Copy link
Member

commented May 1, 2014

Thanks for the follow-up! To be clear, you see the value in eliminating KeyAlgorithm, and see that the proposed mechanism would work, and now we're turning our attention to questions around Key?

if Platform Objects (as Keys truly are and need to be) are not creatable by JS, that's a Good Thing (tm).

That is, a Key represents some set of public, read-only attributes - and some set of internal resources.

Rather than simply allowing the IDL to describe the fact that a Key object can never be constructed by JS - it can only be constructed by 'that thing outside the JS' - and let WebIDL handle checking that the arg instanceof Key is true, which by definition means that the internal property [[handle]] is true.

I could be convinced of this, but I---and your spec readers---will need a lot more background on this to justify it. What are the internal resources you refer to? They can't be [[handle]], since you made that out to be just a boolean. What are they? How does the user agent get access to them? What would they be represented as, if they were formalized as internal properties?

Most importantly, how do other parts of the spec use these internal resources? I'm not as well-versed in the spec as you, but after spending some time looking through the algorithms, I can't see anything in the algorithm steps that refers to using internal resources of the key objects that aren't part of their public interface.

This is why formalizing such resources as [[internal slots]] would be really helpful---then algorithm steps could explicitly refer to them. And indeed, I agree that if all of the algorithms in the spec end up depending on being able to retrieve and manipulate key.[[internalResource]] in some useful way, and key.[[internalResource]] is something that the user can never provide, then it would be easier to just let WebIDL say "no constructor"---or in reality, a constructor that will throw because you didn't pass it the magic resource that you never got access to, since of course objects can't exist without constructors to create them. But since I can't find any evidence of these internal resources being used, I can't see this making any sense at the moment.

That is, the one spec that seems to do what you want, does it differently than how you want.

The recent Font Loading spec more or less follows the internal slots approach, calling them "internal attributes" and using single brackets for reasons I don't understand (EDIT: I asked Tab to switch this over, and he says he will! Yay :). I have seen this in other recent specs too, e.g. service worker has some form of it (although that spec is still very in flux). More importantly, as we move toward a world in which spec authors produce idiomatic JS APIs, this kind of usage will grow.

More importantly, I suspect I may be missing something. Your objections seem to be of the nature that "This isn't valid in JS (or WebIDL), therefore you have to rely on the user agent doing something special" - but how is this not equally true for the internal property slots ([[foo]]), which at least according to http://es5.github.io/#x8.6 are merely syntactic sugar for "The JS engine does something special here, but this is really just semantic and syntactic sugar for 'here be dragons'"

In ES5, this was true: people would just use underscored properties to "hide" the data, which isn't enough for security-level integrity, and is ugly, but is certainly enough to explain the object model. This is similar to how in ES3, there were no getters and setters, so the fact that DOM objects had getter-like abilities was magical and strange, and libraries had to emulate them with .getX() methods.

But in ES6, internal slots can be represented by weak maps. See e.g. especially.

@sleevi

This comment has been minimized.

Copy link

commented May 1, 2014

On Thu, May 1, 2014 at 3:24 PM, Domenic Denicola
notifications@github.comwrote:

Thanks for the follow-up! To be clear, you see the value in eliminating
KeyAlgorithm, and see that the proposed mechanism would work, and now
we're turning our attention to questions around Key?

Of course it would work. We discussed that extensively in the group prior
to implementing KeyAlgorithm. We chose KeyAlgorithm specifically because it
makes it clearer to authors and readers of the spec what the exposed
properties are.

Your change has the clear penalty of making the spec harder to read (from
the perspective of the author), because now they have to dig through prose
to determine what the returned object looks like.

Boris already explained some misconceptions you had regarding
"NoInterfaceObject" and interface inheritance (copies vs prototype chains).

That is, rather than continuing to echo how you would like to see it solved
("return an object"), it would be helpful to understand what your
particular concerns are, so that we can explore whether we can solve this
some other way, or address it via prose while still maintaining some
semblance of readability.

That is, are you solely concerned with the .prototype chain of the
interface objects? Are you worried about the dichotomy between internal
slots and readonly attributes? Is it because you believe every Interface
specified should be Constructible (and if so, let's dig into the why there).

As a spec author and implementor, what I'm trying to accomplish - and what
the WG wanted - was a way to provide authors and implementers clear
guidance on what the return value looks like, without having to dig into a
ton of prose. The WG wanted even less prose than we already have (eg:
just saying 'cross-reference Table 14'), but I objected to that because
that's clearly not something any specification has done.

if Platform Objects (as Keys truly are and need to be) are not creatable
by JS, that's a Good Thing (tm).

That is, a Key represents some set of public, read-only attributes - and
some set of internal resources.

Rather than simply allowing the IDL to describe the fact that a Key object
can never be constructed by JS - it can only be constructed by 'that thing
outside the JS' - and let WebIDL handle checking that the arg instanceof
Key is true, which by definition means that the internal property
[[handle]] is true.

I could be convinced of this, but I---and your spec readers---will need a
lot more background on this to justify it. What are the internal resources
you refer to? They can't be [[handle]], since you made that out to be
just a boolean.

Not sure how you reached that conclusion. Did you conflate with extractable?

The internal resources are opaque handles to the keying material. They are
not the keying material themselves, they are handles that the UA can
redeem for keying material.

This is mentioned in Section 12, where it explicitly describes Key as an
opaque handle. This is also mentioned in Section 4, Section 5.1, and more
importantly - in every single operation.

That is, there's this magic, internal thing where the phrase "with the key
represented by key" is handed to some (internal, opaque to the UA)
cryptographic operation, and the return is some sort of result.

This is a concept familiar to authors who have ever used other
cryptographic APIs - NSS (which uses the standardized PKCS#11), OpenSSL
(the EVP* interface), CDSA/CSSM (which uses CDSA_HANDLE/CSSM_HANDLE),
CommonCrypto (which uses "const void*"), Security.framework (SecKeyRef),
CryptoAPI (which uses HCRYPTKEY), CNG (NCRYPT_KEY_HANDLE)

In all of these APIs, it's a fundamental concept that there's an opaque
"thing" that the underlying system knows how to operate.

That's why it's not possible to create a Key object directly from Key
material - constructing a Key object (via importKey/unwrapKey) is a
transformation of "key material" into a "key handle"

It is explicitly undefined how the UA gets access to them, because
different UAs will use different cryptographic implementations (which this
API is explicitly encouraging UAs not to implement the crypto themselves,
as that would be as crazy as encouraging UAs to implement graphic drivers
themselves for WebGL).

What are they? How does the user agent get access to them? What would they
be represented as, if they were formalized as internal properties?

They cannot be formalized as such. Which is exactly the point. It's a
"thing" that is not common between UAs and systems, and trying to formalize
it into a "thing" is over-specifying in a way that will prevent some
implementations.

In every case I mentioned, it's implemented as either a "void_" or some
form of "pointer-sized integer type" (which is, from a machine level, the
same as a "void_")

Most importantly, how do other parts of the spec use these internal
resources? I'm not as well-versed in the spec as you, but after spending
some time looking through the algorithms, I can't see anything in the
algorithm steps that refers to using internal resources of the key objects
that aren't part of their public interface.

Every place you see the phrase "key represented by key", it's accessing
that internal slot.

This is why formalizing such resources as [[internal slots]] would be
really helpful---then algorithm steps could explicitly refer to them. And
indeed, I agree that if all of the algorithms in the spec end up
depending on being able to retrieve and manipulate
key.[[internalResource]] in some useful way, and key.[[internalResource]]is something that the user can never provide, then it would be easier to
just let WebIDL say "no constructor"---or in reality, a constructor that
will throw because you didn't pass it the magic resource that you never got
access to, since of course objects can't exist without constructors to
create them. But since I can't find any evidence of these internal
resources being used, I can't see this making any sense at the moment.

That is exactly what I'm saying :) all of the algorithm operations (that
take a Key) rely explicitly on converting the JavaScript Key object into an
"opaque handle" that can be given to the "underlying cryptographic
implementation". Search through the spec for the phrase "underlying" to
also find where the spec explicitly hides the magic behind the curtain.

I think one disconnect is that it is explicitly expected that UAs will NOT
implement the cryptographic algorithms themselves. That's bad for a variety
of reasons - correctness, export controls, consistency. Instead, UAs are
expected to talk to some form of cryptographic library - maybe one provided
by the system, the hardware, or, if necessary, the UA itself (ala WebGL
software implementations) - but how is explicitly unspecified.

That's also why there's no normative requirements regarding mandatory to
implement algorithms. A conforming UA could implement no algorithms,
and still be argued as conforming to the API. That's because what
cryptographic operations are permitted depends on a lot of factors outside
the UA's control - the most obvious, of course, being cryptographic laws in
the users' country.

That is, the one spec that seems to do what you want, does it
differently than how you want.

The recent Font Loading spec http://dev.w3.org/csswg/css-font-loading/more or less follows the internal slots approach, calling them "internal
attributes" and using single brackets for reasons I don't understand. I
have seen this in other recent specs too, e.g. service worker has some form
of it (although that spec is still very in flux). More importantly, as we
move toward a world in which spec authors produce idiomatic JS APIs, this
kind of usage will grow.

More importantly, I suspect I may be missing something. Your objections
seem to be of the nature that "This isn't valid in JS (or WebIDL),
therefore you have to rely on the user agent doing something special" - but
how is this not equally true for the internal property slots ([[foo]]),
which at least according to http://es5.github.io/#x8.6 are merely
syntactic sugar for "The JS engine does something special here, but this is
really just semantic and syntactic sugar for 'here be dragons'"

In ES5, this was true: people would just use underscored properties to
"hide" the data, which isn't enough for security-level integrity, and is
ugly, but is certainly enough to explain the object model. This is similar
to how in ES3, there were no getters and setters, so the fact that DOM
objects had getter-like abilities was magical and strange, and libraries
had to emulate them with .getX() methods.

But in ES6, internal slots can be represented by weak maps. See e.g.
especiallyhttps://github.com/domenic/especially/blob/master/meta.js#L18-L47
.

That's great and all, but it doesn't really address my point. ES6 as well
provides the exact same language I was referring to - "These internal
methods are not part of the ECMAScript language. They are defined by this
specification purely for expository purposes."

Within WebCrypto, there is absolutely no way to represent the
"internalHandle" via a weak map. It is a notational type that is
fundamentally outside the realm of the JS engine.

Now, if you wanted to create a polyfill of WebCrypto that was implemented
purely in JS, sure, you could do so. But the WebCrypto spec, as written
today, neither prevents nor requires you to do that, so I'm not sure what
your language change is actually accomplishing - other than trying to
couple the spec even more to the ES engine, which is a concept that WebIDL
seems to desire to avoid.

Since I suspect we still may not be on the same page, my understanding is
that Web API specs are meant to be language agnostic or, at best, language
neutral. WebIDL describes a possible way to implement in ES, but it does
not require that specs themselves be written with the assumption of being
in ES.

Your request for handling [[slots]] is baking in a concept specific to ES
implementations. That's great, but that seems to run counter to how
specifications are written. I can certainly understand a desire to do some
of the XHR-style hand-waving by saying "there's this internal variable on
this internal object, you see, and thats what this attribute returns" - for
which [[slots]] are one possible way of expressing that - but putting
[[slots]] directly in the spec is not something I'm comfortable with unless
the TAG is explicitly saying "All Web Specs must be written for ES
specifically"

Reply to this email directly or view it on GitHubhttps://github.com//issues/3#issuecomment-41963959
.

@domenic

This comment has been minimized.

Copy link
Member

commented May 1, 2014

Of course it would work. We discussed that extensively in the group prior to implementing KeyAlgorithm. We chose KeyAlgorithm specifically because it makes it clearer to authors and readers of the spec what the exposed properties are.

OK. Let's come back to this, if you don't mind, since I'd like to tie off the other issues first.

Every place you see the phrase "key represented by key", it's accessing that internal slot.

Ah! This was what I didn't understand. I agree, now that you have explained it (in great detail, which I seriously appreciate) that there's no point in making these objects constructible, except that it would help explicitness (IMO) and make the spec more terse (as illustrated in the gist). But those are subjective, not normative, things, and don't matter. I'm happy to leave this point alone and make key non-constructible.

BUT! I would encourage you to make this concept more explicit in the spec. "Key represented by key" is really hard to understand, for me at least. I'd expect something near the definition of the Key interface that goes through much of what you've said in this thread, explaining---and preferably naming---the internal opaque data that is being withheld and manipulated. IMO internal slots are a good way of doing this (more on that later); internal slots don't have to have any well-defined type, note. Right now it's a very implicit concept, that I completely missed in my readings and re-readings.

Since I suspect we still may not be on the same page, my understanding is that Web API specs are meant to be language agnostic or, at best, language neutral. WebIDL describes a possible way to implement in ES, but it does not require that specs themselves be written with the assumption of being in ES.

Ah, no! This is not true. WebIDL is explicitly designed for ECMAScript, and we are planning to evolve it that way further. (Indeed, we started a "JSIDL" effort that was a ground-up replacement, but that faltered as its primary instigator ran out of time to work on it, and anyway I at least think an evolutionary approach would work better.)

Now, its historical predecessor OMG IDL---much of which still shows through the skin---was indeed designed to be language neutral. The fact that there is a single "language binding" in WebIDL is just a remnant of evolving OMG IDL, which had multiple language bindings, into WebIDL, by removing all those and leaving the ES one, and then not doing the work necessary to smash down the two conceptual layers into a single one.

Web specs these days are designed specifically for ES, and should only be concerned with exposing themselves in a way that is idiomatic to ES. This is one of the TAG's biggest missions to get across right now, and indeed:

unless the TAG is explicitly saying "All Web Specs must be written for ES specifically"

that is exactly what we are saying. The fact that we haven't gotten this message out is sad, and I will make a note of the fact that we need to try a lot harder to do so.

@sleevi

This comment has been minimized.

Copy link

commented May 1, 2014

On Thu, May 1, 2014 at 4:21 PM, Domenic Denicola
notifications@github.comwrote:

Of course it would work. We discussed that extensively in the group prior
to implementing KeyAlgorithm. We chose KeyAlgorithm specifically because it
makes it clearer to authors and readers of the spec what the exposed
properties are.

OK. Let's come back to this, if you don't mind, since I'd like to tie off
the other issues first.

K, let's visit this now, since I think we're good on the constructible bits
:)

Every place you see the phrase "key represented by key", it's accessing
that internal slot.

Ah! This was what I didn't understand. I agree, now that you have
explained it (in great detail, which I seriously appreciate) that there's
no point in making these objects constructible, except that it would help
explicitness (IMO) and make the spec more terse (as illustrated in the
gist). But those are subjective, not normative, things, and don't matter.
I'm happy to leave this point alone and make key non-constructible.

BUT! I would encourage you to make this concept more explicit in the spec.
"Key represented by key" is really hard to understand, for me at least. I'd
expect something near the definition of the Key interface that goes through
much of what you've said in this thread, explaining---and preferably
naming---the internal opaque data that is being withheld and manipulated.
IMO internal slots are a good way of doing this (more on that later);
internal slots don't have to have any well-defined type, note. Right now
it's a very implicit concept, that I completely missed in my readings and
re-readings.

Now that I grok where you're coming from - and more importantly, the later
points on "Relying on ES is perfectly kosher" - I have no problem giving
this object a name and an internal slot.

Since I suspect we still may not be on the same page, my understanding
is that Web API specs are meant to be language agnostic or, at best,
language neutral. WebIDL describes a possible way to implement in ES, but
it does not require that specs themselves be written with the assumption of
being in ES.

Ah, no! This is not true. WebIDL is explicitly designed for ECMAScript,
and we are planning to evolve it that way further. (Indeed, we started a
"JSIDL" effort that was a ground-up replacement, but that faltered as its
primary instigator ran out of time to work on it, and anyway I at least
think an evolutionary approach would work better.)

Now, its historical predecessor OMG IDL---much of which still shows
through the skin---was indeed designed to be language neutral. The fact
that there is a single "language binding" in WebIDL is just a remnant of
evolving OMG IDL, which had multiple language bindings, into WebIDL, by
removing all those and leaving the ES one, and then not doing the work
necessary to smash down the two conceptual layers into a single one.

Web specs these days are designed specifically for ES, and should only be
concerned with exposing themselves in a way that is idiomatic to ES. This
is one of the TAG's biggest missions to get across right now, and indeed:

unless the TAG is explicitly saying "All Web Specs must be written for ES
specifically"

that is exactly what we are saying. The fact that we haven't gotten this
message out is sad, and I will make a note of the fact that we need to try
a lot harder to do so.


Reply to this email directly or view it on GitHubhttps://github.com//issues/3#issuecomment-41968155
.

Works for me. ES6 all the way. Also solves the language specifying how JWK
is imported/exported (as an object) and how JSON parsing is handled (since
now I can just refer to ES's JSON.parse()/JSON.stringify() )

@domenic

This comment has been minimized.

Copy link
Member

commented May 1, 2014

Sweet! Progress.

I think the important thing to keep in mind about the constructible bits is that it's not "no constructor"---it's "there is a constructor (since otherwise the objects couldn't exist), but only the implementation has access to the correct values to call it without getting an error thrown."

That is, rather than continuing to echo how you would like to see it solved ("return an object"), it would be helpful to understand what your particular concerns are, so that we can explore whether we can solve this some other way, or address it via prose while still maintaining some semblance of readability.

OK. Here are my particular concerns:

  • Using classes to encapsulate data records, instead of data and behavior, is generally a bad idea. This is somewhat of a general OOP principle, but it applies especially in JavaScript, where creating data records via object literals is so easy.
  • A class, in JavaScript, isn't a template for an object shape. It's a function (the constructor) with a linkage to a particular set of shared properties, usually accessors and methods (the prototype, given by Constructor.prototype). New instances of the class are then created that delegate to those shared properties. There's an intimate linkage between these three entities: Constructor.prototype.constructor === Constructor, and Object.getPrototypeOf(instance) === Constructor.prototype. Even if the constructor doesn't exist as a property of window, it still exists, and is a primary entity in the instance-prototype-constructor system that programmers interact with.
  • By creating classes that are just a bunch of accessor properties, what you are doing is saying: on a given instance, store the data in internal slots (or, in common JS programming, underscored data properties). Then, when someone accesses instance.accessor, go delegate to the shared prototype which contains that accessor function. The accessor will look at its this instance and pull off the internal slot value, and return that to the caller. This is a lot of unnecessary complication in the mental model compared to just "pull the data property off of the object!"
  • Accessor properties have worse usability than data properties. For example, copying an object with accessor properties, and a custom prototype containing them, is not possible generically. And even if someone was trying to do it specifically coding for your objects, they'd need a constructor! Otherwise the objects come out of nowhere.
  • From the point of view of spec readability, duplicating the same data in two parallel object hierarchies---one a dictionary, and one a class---is quite confusing for the reader. What's the difference between the two? Well, one of them is allowed to be returned by a property in WebIDL, so we use it for properties; the other is not, so we use it for parameters. That's not a good user story :(

As for ways to make it easier to determine the return value from the IDL, I guess I am not sure how KeyAlgorithm is much better than object, given that the definition for KeyAlgorithm is just { name }. In reality, with the current spec, it's some subclass of KeyAlgorithm. But you'd need to note that in prose anyway if you wanted a reader to understand it...

@sleevi

This comment has been minimized.

Copy link

commented May 2, 2014

On Thu, May 1, 2014 at 4:45 PM, Domenic Denicola
notifications@github.comwrote:

Sweet! Progress.

I think the important thing to keep in mind about the constructible bits
is that it's not "no constructor"---it's "there is a constructor (since
otherwise the objects couldn't exist), but only the implementation has
access to the correct values to call it without getting an error thrown."

That is, rather than continuing to echo how you would like to see it
solved ("return an object"), it would be helpful to understand what your
particular concerns are, so that we can explore whether we can solve this
some other way, or address it via prose while still maintaining some
semblance of readability.

OK. Here are my particular concerns:

  • Using classes to encapsulate data records, instead of data and
    behavior, is generally a bad idea. This is somewhat of a general OOP
    principle, but it applies especially in JavaScript, where creating data
    records via object literals is so easy.

Eh, this is the attempt at the C++ equivalent of 'structs' (or C, for that
matter), where encapsulating data records via objects provides strong
typing, as opposed to treating all object types as "void*" (what I see as
the moral/spiritual equivalent to using Object in JS)

  • A class, in JavaScript, isn't a template for an object shape. It's a
    function (the constructor) with a linkage to a particular set of shared
    properties, usually accessors and methods (the prototype, given by
    Constructor.prototype). New instances of the class are then created
    that delegate to those shared properties. There's an intimate linkage
    between these three entities: Constructor.prototype.constructor ===
    Constructor, and Object.getPrototypeOf(instance) ===
    Constructor.prototype. Even if the constructor doesn't exist as a
    property of window, it still exists, and is a primary entity in the
    instance-prototype-constructor system that programmers interact with.
  • By creating classes that are just a bunch of accessor properties,
    what you are doing is saying: on a given instance, store the data in
    internal slots (or, in common JS programming, underscored data properties).
    Then, when someone accesses instance.accessor, go delegate to the
    shared prototype which contains that accessor function. The accessor will
    look at its this instance and pull off the internal slot value, and
    return that to the caller. This is a lot of unnecessary complication in the
    mental model compared to just "pull the data property off of the object!"

Whose mental model? From the perspective of the developer reading this, or
the implementor implementing, it's clear exactly what properties exist in
the object purely by reading the IDL (rather than the prose).

  • Accessor properties have worse usability than data properties. For
    example, copying an object with accessor properties, and a custom prototype
    containing them, is not possible generically. And even if someone was
    trying to do it specifically coding for your objects, they'd need a
    constructor! Otherwise the objects come out of nowhere.

Isn't this exactly the short-hand that WebIDL already solves with respect
to Interface objects?

  • From the point of view of spec readability, duplicating the same
    data in two parallel object hierarchies---one a dictionary, and one a
    class---is quite confusing for the reader. What's the difference between
    the two? Well, one of them is allowed to be returned by a property in
    WebIDL, so we use it for properties; the other is not, so we use it for
    parameters. That's not a good user story :(

Absolutely agreed. This was the main complaint with KeyAlgorithm vs
Algorithm - that it duplicates the hierarchy, only swapping dictionaries
for interfaces.

Combined with the fact that we're ditching Algorithm as an input in the
WebIDL (in favour of the 'object' & coercion mentioned earlier and
discussed with Boris), the effect I fear this has on the spec is that the
API looks something like

void* DoSomething(void* input);

With the type of |input| and the return value being documented in prose. Is
it legal? Absolutely! Is it a good documentation/developer-friendly
pattern? I wouldn't think so.

As for ways to make it easier to determine the return value from the IDL,
I guess I am not sure how KeyAlgorithm is much better than object, given
that the definition for KeyAlgorithm is just { name }. In reality, with
the current spec, it's some subclass of KeyAlgorithm. But you'd need to
note that in prose anyway if you wanted a reader to understand it...


Reply to this email directly or view it on GitHubhttps://github.com//issues/3#issuecomment-41969776
.

Notationally, what I would think is needed - either of WebIDL or in some
sort of TAG-blessed synatactic spec sugar - is a way to document the
'properties on a given Object that an implementation MUST expose in order
to be spec compliant'. To me, notwithstanding the .prototype hijinks (which
are attempted to be resolved with [NoInterfaceObject], since only the
most-derived Prototype is ever exposed), we have that means through
Interface.

@domenic

This comment has been minimized.

Copy link
Member

commented May 2, 2014

Notationally, what I would think is needed - either of WebIDL or in some sort of TAG-blessed synatactic spec sugar - is a way to document the 'properties on a given Object that an implementation MUST expose in order to be spec compliant'.

I agree, we definitely need this. And if it takes me doing a pull request to WebIDL to make it happen, I'm all in. But before we solve that, however, how would this help web crypto?

It seems like the most-common ancestor is Algorithm/KeyAlgorithm, both of which are just { name }. Do you think that { name } DoSomething({ name } input), so to speak, is really that much better than void* DoSomething(void* input)? Again, I feel like people will see Algorithm DoSomething(Algorithm input), go look at the definition of Algorithm, and get mega-confused. (My first reaction was, why do we have this type at all? Why not just use strings?) You'd need prose to un-confuse them, which puts us back where we started.

@sleevi

This comment has been minimized.

Copy link

commented May 2, 2014

On Thu, May 1, 2014 at 5:05 PM, Domenic Denicola
notifications@github.comwrote:

Notationally, what I would think is needed - either of WebIDL or in some
sort of TAG-blessed synatactic spec sugar - is a way to document the
'properties on a given Object that an implementation MUST expose in order
to be spec compliant'.

I agree, we definitely need this. And if it takes me doing a pull request
to WebIDL to make it happen, I'm all in. But before we solve that, however,
how would this help web crypto?

It seems like the most-common ancestor is Algorithm/KeyAlgorithm, both of
which are just { name }. Do you think that { name } DoSomething({ name }
input), so to speak, is really that much better than void*
DoSomething(void* input)? Again, I feel like people will see Algorithm
DoSomething(Algorithm input), go look at the definition of Algorithm, and
get mega-confused. (My first reaction was, why do we have this type at all?
Why not just use strings?) You'd need prose to un-confuse them, which puts
us back where we started.


Reply to this email directly or view it on GitHubhttps://github.com//issues/3#issuecomment-41970922
.

I agree, you still need Prose to explain how it's handled within the actual
algorithm execution - eg: to know that it's RsaResult DoSomething(RSAParams
input)

However, having some way to normatively provide a type notation for
RsaResult - eg: these are the attributes that all such objects return - as
opposed to saying "construct a new Object. Call defineOwnProperty(foo).
Call defineOwnProperty(bar); call defineOwnProperty(baz);" - seems far
better.

@domenic

This comment has been minimized.

Copy link
Member

commented May 2, 2014

However, having some way to normatively provide a type notation for RsaResult

Oh! Isn't that just dictionaries? You wouldn't be able to declare them in the signature, due to WebIDL limitations, but you can still use them in your algorithms. The patch I gave above in the gist does exactly that, reusing the RsaKeyParams dictionary in place of RsaKeyAlgorithm, even though the return type is object.

@sleevi

This comment has been minimized.

Copy link

commented May 2, 2014

On Thu, May 1, 2014 at 5:13 PM, Domenic Denicola
notifications@github.comwrote:

However, having some way to normatively provide a type notation for
RsaResult

Oh! Isn't that just dictionaries? You wouldn't be able to declare them in
the signature, due to WebIDL limitations, but you can still use them in
your algorithms. The patch I gave above in the gist does exactly that,
reusing the RsaKeyParams dictionary in place of RsaKeyAlgorithm, even
though the return type is object.


Reply to this email directly or view it on GitHubhttps://github.com//issues/3#issuecomment-41971380
.

Ah! I missed that http://heycam.github.io/webidl/#es-dictionary specifies
how to convert an IDL dictionary into an Object

In that case, yes, simply converting to "object" should be sufficient,
since the prose will be described using IDL dictionaries, and it should be
'obvious' that when it says "set X [[which is specified as an object
attribute]] to Y [[which is prosaically described as an IDL dictionary]]"
means "run the WebIDL conversion steps.

@annevk

This comment has been minimized.

Copy link
Member

commented May 2, 2014

@sleevi using new Key() for something UA-managed is fine. That is pretty much how new Image() and other constructors that exist work. It also does not mean that such an object can be spoofed if you define it through IDL. E.g. if you define a method that takes a Key it will throw unless it was created through the constructor defined by the specification.

@sleevi

This comment has been minimized.

Copy link

commented May 2, 2014

@annevk - in the case of new Image(), there's a path to turn something
caller supplied (eg: a URL) into something UA managed (eg: image
resources). There's no such way to ever do so with a "new Key", because the
resource handle is never something the caller can obtain, nor can they
specify in any meaningful syntax (URI, integer, etc). So if there is a
constructor, it would have to never be exposed to script - or would have to
have a way of expressing in WebIDL the magic mystery internal property.
That either has to be supplied in the ctor, which would implode most UAs'
bindings systems - or be specified as a magic internal [[atribute]], which
leads to the problem that Domenic was trying to solve with "don't have
half-created objects". It would mean script could "new Key", but then every
handler would have to check if the internal property exists (meaning UA
created) or doesn't (meaning script created)

@domenic - A question/problem with the whole [[internal attributes]] thing.

Let's say I specify that Key.algorithm returns the [[algorithm]] internal
attribute of Key. [[algorithm]] is specified as an IDL dictionary, and
because Key.algorithm is specified as object, I go through the WebIDL
conversion rules of converting a dictionary to an object.

When does this conversion happen? Is it legal for a UA to cache? As far as
I can tell from the specs, the answer is "every time" and "no"

This means if I write code like

window.crypto.subtle.importKey(...).then(function(key) {
var x = key.algorithm;  // converts [[algorithm]] to object
var y = key.algorithm.name;  // converts [[algorithm]] to object, then
returns the DOMString
var z = (key.algorithm === key.algorithm);  // false - performs two
conversions of [[algorithm]] and creates distinct objects

Apologies if the above is syntactically incorrect, but hopefully you see
the issue I'm trying to highlight. If I prosaically specify key.algorithm
to return the [[algorithm]] internal attribute, and the [[algorithm]]
internal attribute is a Web IDL dictionary (which would be the implication
if I used language like Set the [[algorithm]] internal attribute to rsaKeyAlgorithm), then it's performing the Web IDL steps every time.

Thoughts?

@domenic

This comment has been minimized.

Copy link
Member

commented May 3, 2014

When does this conversion happen? Is it legal for a UA to cache? As far as I can tell from the specs, the answer is "every time" and "no"

Yeah, good catch. This is why the prose I outlined in the gist says "After being set, which is only possible by specification text, it should return the set object for each getter invocation." If you were using the internal slot approach, you'd have say something similar, e.g. "It should return the same object representation of [[algorithm]] for each getter invocation, and not run the dictionary-to-object conversion each time." @bzbarsky has specifically said he'd like to introduce an attribute to WebIDL, similar to one they have in Gecko, for the purpose of moving this out of prose.

I wonder if it would work to have [[algorithm]] be an object instead? That is, you would do the conversion at set time, not at get-time. So instead of "Set the [[algorithm]] internal slot to rsaKeyAlgorithm," you'd say, "Set the [[algorithm]] internal slot to the result of converting rsaKeyAlgorithm to its object representation." I am not sure if this would be better though in terms of spec maintenance and readability, but it might be.

@mnot

This comment has been minimized.

Copy link
Member

commented Sep 30, 2014

Discussed at London F2F; can close.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.