#3: Web Crypto API
Discussions
Comment by @domenic Mar 28, 2014 (See Github)
I hear that this is getting urgent; multiple browsers are very close to shipping.
Comment by @domenic Mar 28, 2014 (See Github)
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.
Comment by @sleevi Mar 28, 2014 (See Github)
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.
Comment by @sleevi Mar 28, 2014 (See Github)
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 :)
Comment by @domenic Mar 28, 2014 (See Github)
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.
Comment by @sleevi Apr 21, 2014 (See Github)
Any further updates?
Comment by @domenic Apr 21, 2014 (See Github)
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.
Comment by @domenic Apr 28, 2014 (See Github)
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; useTypeError
. - 9.2.1 use
RangeError
notQuotaExceededError
. - As-is, the
Algorithm
dictionary seems useless, as it only contains aname
value. (Just use a string in that case.) I see from the open issue that it might also containparams
, 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 forKey
. 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 constructWindowCrypto
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 becomesignature => 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.
Comment by @sleevi Apr 28, 2014 (See Github)
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.
- 12.4 monkey-patches http://annevankesteren.nl/2014/02/monkey-patchstructured clone, which is bad.
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/w3ctag/spec-reviews/issues/3#issuecomment-41521737 .
Comment by @domenic Apr 28, 2014 (See Github)
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.
Comment by @sleevi Apr 29, 2014 (See Github)
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/w3ctag/spec-reviews/issues/3#issuecomment-41625306 .
Comment by @sleevi Apr 29, 2014 (See Github)
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/w3ctag/spec-reviews/issues/3#issuecomment-41625306 .
Comment by @domenic Apr 29, 2014 (See Github)
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).
Comment by @domenic Apr 30, 2014 (See Github)
@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.
Comment by @sleevi May 1, 2014 (See Github)
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:
- Let key be a new Key object as if created by new Key(foo, bar, baz, bat)
- 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:
- 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/w3ctag/spec-reviews/issues/3#issuecomment-41757833 .
Comment by @domenic May 1, 2014 (See Github)
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.
Comment by @sleevi May 1, 2014 (See Github)
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/w3ctag/spec-reviews/issues/3#issuecomment-41963959 .
Comment by @domenic May 1, 2014 (See Github)
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.
Comment by @sleevi May 1, 2014 (See Github)
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/w3ctag/spec-reviews/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() )
Comment by @domenic May 1, 2014 (See Github)
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
, andObject.getPrototypeOf(instance) === Constructor.prototype
. Even if the constructor doesn't exist as a property ofwindow
, 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 itsthis
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...
Comment by @sleevi May 2, 2014 (See Github)
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/w3ctag/spec-reviews/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.
Comment by @domenic May 2, 2014 (See Github)
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.
Comment by @sleevi May 2, 2014 (See Github)
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/w3ctag/spec-reviews/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.
Comment by @domenic May 2, 2014 (See Github)
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
.
Comment by @sleevi May 2, 2014 (See Github)
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/w3ctag/spec-reviews/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.
Comment by @annevk May 2, 2014 (See Github)
@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.
Comment by @sleevi May 2, 2014 (See Github)
@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?
Comment by @domenic May 3, 2014 (See Github)
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.
Comment by @mnot Sep 30, 2014 (See Github)
Discussed at London F2F; can close.
OpenedJul 1, 2013
Editor's draft: https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html