- From: Vijay Bharadwaj <vijaybh@microsoft.com>
- Date: Sat, 4 Jun 2016 21:14:42 +0000
- To: "Powers, Adam" <adam@fidoalliance.org>, "Hodges, Jeff" <Jeff.Hodges@paypal.com>
- CC: W3C Web Authn WG <public-webauthn@w3.org>
- Message-ID: <3e959b27eab04fae9470a41826589123@microsoft.com>
>>> * If whitelist is defined and non-empty, <<<< optionally >>> execute a >>> platform-specific procedure to determine which of these >>> credentials can possibly be present on this >>> authenticator.>>>> >> >> Implies cred id was stored in makeCred? >> Maybe some hint in makeCred? > > yes, the authnr generates and stores a tuple of { private key, cred id, > cred type, RP ID } -- this is stated in "The authenticatorMakeCredential > operation" <https://w3c.github.io/webauthn/#op-make-cred>, though it > could perhaps be stated more rigorously. My understanding is that this section is talking about user agent behavior, not authenticator behavior. This step seems to imply that upon a successful makeCredential() call, the user agent stored some tuple like { authenticator identifier, credential id, credential type, RP ID }. Now, in getAssertion() the user agent is going to do something like iterate through its list of available authenticators and see if it has a matching { credential id, credential type } that matches the ids / types in the whitelist. If this authenticator doesn’t have any potential credential ids that are available in the whitelist, it should be ignored. If my understanding is correct, there should be a corresponding step in the algorithm of Section 3.1.1, where makeCredential() stores the credential id associated with the authenticator. This seems to intersect with something Dirk brought up in https://lists.w3.org/Archives/Public/public-webauthn/2016May/0281.html It does seem like you’ve hit another case where external and embedded authenticators behave differently. For an embedded authenticator, it would be reasonable to assume that the user agent can reliably know a mapping between credential ID and authenticator. But for external authenticators (which can roam) this may not be the case since the credential may have been created somewhere else, the authenticator might not be plugged in at the moment, etc. Do you think this indicates we should support (e.g. by adding an optional parameter) the ability to request an embedded vs. external authenticator in makeCredential? If so, should there also be a way for an RP to detect the presence of embedded authenticators? (You can’t detect the presence of external authenticators because they may just not be plugged in right now, or not tapped to the NFC reader, etc. – at best, you can detect if the client supports the notion of external authenticators.) For getAssertion, of course, nothing additional is needed. In keeping with our design philosophy for the rest of the API, we’d expect the RP to keep track of which credentials were created on embedded vs. external authenticators. Thoughts? From: Powers, Adam [mailto:adam@fidoalliance.org] Sent: Friday, June 03, 2016 4:57 PM To: Hodges, Jeff <Jeff.Hodges@paypal.com> Cc: W3C Web Authn WG <public-webauthn@w3.org> Subject: Re: Notes from WebAuthn Review Hi Jeff, my sincerest thanks for taking the time to reformat and provide such a comprehensive reply to my comments. That was obviously no small feat -- and I will try to follow the format you used for future feedback. Below are my responses to your comments, which I've highlighted in blue to make them easier to skim through and find. I've deleted all the issues that I consider closed based on your feedback. >>> Web Authentication: A Web API >>> for accessing scoped credentials >>> Editor's Draft, 13 May 2016 >>> >>> [...] >>> >>> >>> >>> Abstract >> >> This jumps right into using jargon -- >> given the potential audience, do we want >> to ease them into he jargon? > > We have refined the language and terminology somewhat in > https://github.com/w3c/webauthn/pull/109, which is now merged to master > and available as https://w3c.github.io/webauthn/ - please check that out > > but that said, this is a web api spec and is assuming relatively > sophisticated readers. We can do really soft hand-holding in an > "explainer doc" as has been discussed (volunteers? :) > > also, i expect this spec to go thru further editorial evolution which > ought to improve its understandability. I’m not quite sure who the target audience is, but I was assuming that it was as much consumers of the API as it is browser vendors. If that’s true, it might help to > ease the API consumers into our nomenclature. Agreed that we can iterate and evolve. >>> This specification defines an API for web pages to access scoped >>> credentials through JavaScript, for the purpose of strongly >>> authenticating a user. >>> >>> [...] >>> >>> § 1.1. Registration (embedded authenticator mode) >> >> Diagrams? > > do you mean diagrams of some person messing with their phone+laptop or > do you mean protocol flow diagrams ? > > if the latter, I'm working on some and will offer them up, tho it will > be up to the group whether we bake them directly into the spec... > > WebAuthn-Figures-00 > <https://docs.google.com/presentation/d/1om__oSew4n48MK_Qcc8deq6hCZ6720-Zvv1PdK0CrjA> I was thinking more of higher-level sequence diagrams to help illustrate the use cases. Something along the lines of: user+browser+authenticator+relying party. I think the diagram you are working on is great and needed, but probably more detail than needed for an introduction. Happy to put together an example of what I'm thinking if that helps. >>> [...] >>> >>> interface ScopedCredentialInfo { >>> readonly attribute Credential credential; >>> readonly attribute any publicKey; >>> readonly attribute WebAuthnAttestation <<<<attestation;>>>> >>> }; >> >> If attestation is null, there would >> be no clientDataHash. Negative >> security ramifications? > > yes, there would be negative security ramifications if there is no > supplied attestation (i.e., attestation == null), and the RP were to to > ahead and store the credential and the publicKey for later use. > > However, per web IDL, the attribute members declared within > ScopedCredentialInfo are *not* "nullable", so your concern is being > implicitly addressed. And it is also nominally addressed in the > registration operation processing rules I believe. There is consideration that attestation may be nullable (and the Edge implementation currently returns a null attestation), so this was largely just thinking ahead. See also: https://github.com/w3c/webauthn/issues/86 >>> On success, the promise will be resolved with a >>> ScopedCredentialInfoobject describing the newly created >>> credential. >>> >>> This method takes the following parameters: >>> [...] >>> >>> When this method is invoked, the user agent MUST execute the >>> following algorithm: >>> >>> 1. If credentialTimeoutSeconds was specified, check if its value >>> lies within a reasonable range as defined by the platform and if >>> not, correct it to the closest value lying within that range. Set >>> adjustedTimeout to this adjusted value. If >>> credentialTimeoutSeconds was not specified then set adjustedTimeout >>> to a platform-specific default. >>> 2. Let promise be a new Promise. Return promise and start a timer >>> for adjustedTimeout seconds. Then asynchronously continue executing >>> the following steps. 3. Set callerOrigin to the origin of the >>> caller. Derive the RP ID from callerOrigin by computing the "public >>> suffix + 1" or "PS+1" (which is also referred to as the "Effective >>> Top-Level Domain plus One" or "eTLD+1") part of callerOrigin [PSL]. >>> Set rpId to the RP ID. >>> 4. Initialize issuedRequests to an empty list. >>> 5. Process each element of cryptoParameters using the following >>> steps, to produce a new sequence normalizedParameters: >> >> Additional description of what >> normalization is trying to >> accomplish would be helpful > > see https://www.w3.org/TR/WebCryptoAPI/#algorithm-concepts-normalization > > that said, webcrypto normalization is fairly gnarly, tho I spent some > time figuring it out a while back and have info to send to this mailing > list about it. Even just quoting WebCrypto 18.4.1 would be helpful in understanding the intent: “The AlgorithmIdentifier typedef permits algorithms to either be specified as a DOMString or an object. The usage of DOMString is to permit authors a short-hand for noting algorithms that have no parameters (e.g. SHA-1). The usage of object is to allow an Algorithm (or appropriate subclass) to be specified, which contains all of > the associated parameters for an object. “Because of this, it's necessary to define the algorithm for converting an AlgorithmIdentifier into an appropriate dictionary that is usable with this API. This algorithm must be extensible, so as to allow new cryptographic algorithms to be added, and consistent, so that Web IDL type mapping can occur before any control is > returned to the calling script, which would potentially allow the mutation of parameters or the script environment.” >>> Let current be the currently selected element of cryptoParameters. >>> [...] >>> >>> 6. If blacklist is undefined, set it to the empty list. >>> 7. If credentialExtensions was specified, process any extensions >>> supported by this client platform, to produce the extension data >>> that needs to be sent to the authenticator. Call this data >>> clientExtensions. >>> 8. For each embedded or external authenticator currently available on >>> this platform: asynchronously invoke the authenticatorMakeCredential >>> operation on that authenticator with callerOrigin, rpId, >>> accountInformation, normalizedParameters, blacklist, >>> attestationChallenge and clientExtensions as parameters. Add a >>> corresponding entry to issuedRequests. >>> >>> 9. While issuedRequests is not empty, perform the following actions >>> depending upon the adjustedTimeout timer and responses from the >>> authenticators: >>> >>> If the adjustedTimeout timer expires, then for each entry in >>> issuedRequests invoke the authenticatorCancel operation on that >>> authenticator and remove its entry from the list. >>> >>> If any authenticator returns a status indicating that the user >>> cancelled the operation, delete that authenticator's entry from >>> issuedRequests. <<<< For each remaining entry in issuedRequests >>> invoke the authenticatorCancel operation on that authenticator >>> and remove its entry from the list. >>>> <=== The intent is that >> cancel on one >> results in cancelling all? > > what you mean "cancel on one" is perhaps mistaken. The intent is that > the user has canceled *the entire registration operation*. I think we are saying the same thing. Here is my understanding: Assuming that a system has recognized three available authenticators, when a makeCredential() request is called all three authenticators will display a message asking a user to approve making a credential. If a user were to select cancel / no / decline on one > authenticator, a authenticatorCancel() request would be sent to the two other authenticators and the entire registration operation would effectively be cancelled. As a separate question, which occurred to me later, what happens when the first authenticator succeeds? If a user selects ok / yes / approve, do the other two authenticators still display their confirmation messages and allow additional registration responses? Or should the client send authenticationCancel() to the two other authenticators after the first successful result? I'm assuming it's the latter, but I can imagine arguments for the former. >>> If any authenticator returns an error status, delete the >>> corresponding entry from issuedRequests. If any authenticator >>> indicates success, create a new ScopedCredentialInfoobject named >>> value and populate its fields with the values returned from the >>> authenticator. Resolve promise with value and terminate this >>> algorithm. >>> >>> 10. Resolve promise with a DOMException whose name is >>> "NotFoundError", and terminate this algorithm. >>> >>> During the above process, the user agent SHOULD show some UI to the >>> user to guide them in the process of selecting and authorizing an >>> authenticator. >> >> ...but requests are sent to all >> authenticators in #8 above? > > yes, authenticatorMakeCredential() is invoked asynchronously on each > authnr, however this is a protocol spec and we are not specifying > particular UI/UX. the parag you are referring to is simply > implementation guidance. each user agent will do their best to craft a > worthwhile UX (user experience). Sorry, my question wasn’t very clear. Here’s the UX as it seems to be described in this section: 1. User navigates to a website and selects “register”, which calls the makeCredential() API of the user agent 2. The user agent shows a UI allowing the user to select which authenticator should be used 3. The user agent then calls authenticatorMakeCredential() on all the available authenticators, and all of the user’s authenticators display a message asking the user to accept / reject the registration request Why does the user select an authenticator in step #2, only to have all the authenticators request registration in step #3? Was step #2 intended to be a filter for step #3? If it was, then step #3 should be "calls authenticatorMakeCredential() on all the authenticators selected by the user"? >>> * If whitelist is defined and non-empty, <<<< optionally >>> execute a >>> platform-specific procedure to determine which of these >>> credentials can possibly be present on this >>> authenticator.>>>> >> >> Implies cred id was stored in makeCred? >> Maybe some hint in makeCred? > > yes, the authnr generates and stores a tuple of { private key, cred id, > cred type, RP ID } -- this is stated in "The authenticatorMakeCredential > operation" <https://w3c.github.io/webauthn/#op-make-cred>, though it > could perhaps be stated more rigorously. My understanding is that this section is talking about user agent behavior, not authenticator behavior. This step seems to imply that upon a successful makeCredential() call, the user agent stored some tuple like { authenticator identifier, credential id, credential type, RP ID }. Now, in getAssertion() the user agent is going to do something like iterate through its list of available authenticators and see if it has a matching { credential id, credential type } that matches the ids / types in the whitelist. If this authenticator doesn’t have any potential credential ids that are available in the whitelist, it should be ignored. If my understanding is correct, there should be a corresponding step in the algorithm of Section 3.1.1, where makeCredential() stores the credential id associated with the authenticator. >>> § 3.3. User Account Information (dictionary Account) >>> >>> This dictionary is used by the caller to specify information about >>> the user account and WebAuthn Relying Party with which a credential >>> is to be associated. It is intended to help the authenticator in >>> providing a friendly credential selection interface for the user. >>> >>> The rpDisplayName member contains the friendly name of the >>> WebAuthn Relying Party, such as "Acme Corporation", "Widgets Inc" >>> or "Awesome Site". >>> >>> The displayName member contains the friendly name associated with >>> the user account by the WebAuthn Relying Party, such as "John P. >>> Smith". >>> >>> The name member contains a detailed name for the account, such as >>> "john.p.smith@example.com<mailto:john.p.smith@example.com>". >>> >>> The id member contains an identifier for the account, stored for >>> the use of the WebAuthn Relying Party. This is not meant to be >>> displayed to the user. >>> >>> The imageURL member contains a URL that resolves to the user's >>> account image. This may be a URL that can be used to retrieve an >>> image containing the user's current avatar, or a data URI that >>> contains the image data. >> >> Other fields okay? > > I am unsure what you are asking here. Can User Account Information be expanded to include other attributes, such as Relying Party Logo URL? Or is it strictly limited to the attributes that have been named? >>> § 3.5. WebAuthn Assertion (interface WebAuthnAssertion) >>> >>> [...] >>> >>> >>> § 3.7. Credential Attestation Statement (interface WebAuthnAttestation) >>> >> may? >>> Authenticators <<<< also provide >>>> some form of attestation. The >>> basic requirement is that the authenticator can produce, for each > > It should perhaps be "..MUST also provide..", and have a link to the > {#attestation-models} section. > > https://github.com/w3c/webauthn/issues/115 Unless nullable attestation is okay, then this would be changed to “Authenticators may also provide”. See https://github.com/w3c/webauthn/issues/86 >>> credential public key, attestation information that can be verified >>> by a WebAuthn Relying Party. Typically, this information contains a >>> signature by an attesting key over the attested public key and a >>> challenge, as well as a certificate or similar information providing >>> provenance information for the attesting key, enabling a trust >>> decision to be made. >>> >>> [...] >>> >>> § 4. WebAuthn Authenticator model >>> >>> [...] >>> >>> § 4.1. Authenticator operations >>> >>> A client must connect to an authenticator in order to invoke any of >>> the operations of that authenticator. This connection defines an >>> authenticator session. An authenticator must maintain isolation >>> between sessions. It may do this by only allowing one session to >>> exist at any particular time, or by providing more complicated >>> session management. >>> >>> The following operations can be invoked by the client in an >>> authenticator session. >>> >>> § 4.1.1. The authenticatorMakeCredential operation >> >> Should clientDataHash be passed >> in, or is this assuming that the >> authn calculates it? > > The authenticator marshalls the input params of this operation into the > clientData structure and computes the clientDataHash. > > See: https://w3c.github.io/webauthn/#authenticator-signature Per WebAuthn Section 5.2 “To save bandwidth and processing requirements on the authenticator, the client platform hashes the client data and sends only the result to the authenticator.” I suppose we could interpret 5.1 as sending data down to an intermediate-level driver between the browser and the authenticator, and that intermediate-level driver could create clientDataJSON and clientDataHash and send just the clientDataHash to the authenticator. But that intermediate-level driver would also need tokenBinding information for the clientDataJSON (currently a missing argument to authenticatorGetAssertion). It would also be operating outside the Secure Context and I have a hard time understanding why we would be creating the clientDataJSON and clientDataHash outside the Secure Context. My guess is that the arguments to authenticatorGetAssertion() should be: RP ID, clientDataHash, whitelist, extensions. >>> Before making a request to an authenticator, the client platform >>> layer SHALL perform the following steps. >>> >>> 1. Represent the parameters passed in by the RP in the form of a >>> ClientDatastructure. >>> 2. Let clientDataJSON be the UTF-8 encoded JSON serialization > [RFC7159] >>> of this ClientData dictionary. >>> 3. Let clientDataHash be the hash (computed using hashAlg) of >>> clientDataJSON, as an array. >>> >>> The clientDataHash value is delivered to the authenticator. >>> >>> The hash algorithm hashAlg used to compute clientDataHash is >>> included in the ClientData object. This way it is available to the >>> WebAuthn Relying Party and it is also hashed over when computing >>> clientDataHash and hence anchored in the signature itself. >>> >>> A raw cryptographic signature must assert the integrity of both >>> the client data and the authenticator data. Thus, an authenticator >>> SHALL compute a signature over the concatenation of the >>> authenticatorData and the clientDataHash. >> >> There's no mention of incrementing >> the counter? > > that could perhaps be mentioned in the more general > authenticatorMakeCredential operation section. > >> Also, should >> authenticatorMakeCredential mention >> storing / retrieving it? > > counters are already mentioned in the various attestation sections -- is > this not sufficient? Searching for “counter” across the entire document only shows a few definitions of counters, not any description of their use. Am I missing something? And what about for assertion signatures – every time an authenticator asserts it should grab the last counter, bump it, sign it, and store the new value, right? >>> § 4.3. Credential Attestation Statements >> >> Thinking ahead to future >> attestation formats, is there >> a requirement that they MUST >> sign over clientDataHash? > > nominally, yes -- clientData comprises the contextual bindings gathered > as the challenge+params wends its way from the RP-server-side (RPSS) > down to the authenticator. marshaling it into clientDataJSON, hashing > it, and signing over it, and then returning the plaintext collected > clientDataJSON as well as the signature gives the RPSS assurance that it > was indeed its challenge+params that the authnr received and that they > weren't fiddled with during their travels. this is explained in > https://w3c.github.io/webauthn/#sec-client-data but perhaps could be > expanded upon (contributions welcome :) > > if you examine all three presently-defined attestation types (packed, > TPM, android), you'll see we've carefully spec'd how to incorporate > clientData into the inputs to the data the authenticator signs over. > > to answer your question, yes, perhaps stipulating the importance of any > new attestation types including some form of contextaul binding a la > clientdata should be added. (contributions welcome :) My point was what you said in your last paragraph. Should we clarify the requirements of future attestation formats here or maybe that happens in the definition of an IANA registry (assuming that attestation formats eventually end up in a registry)? >>> Surrogate Basic Attestation >>> >>> [...] >>> >>> § 4.3.4. Security Considerations >>> >>> § 4.3.4.1. Privacy >>> >>> Attestation keys may be used to track users or link various online >>> identities of the same user together. This may be mitigated in several >>> ways, including: >>> >>> [...] >>> >>> 4.3.4.2. Attestation Certificate and Attestation Certificate CA >>> Compromise >>> >>> [...] >>> >>> If attestation certificate validation fails due to a revoked >>> intermediate attestation CA certificate, and the WebAuthn Relying >>> Party's policy requires rejecting the registration/authentication >>> request in these situations, then it is recommended that the >>> WebAuthn Relying Party also un-registers (or marks as "surrogate >>> attestation" >>> (see §4.3.1 Attestation Models), <<<< policy permitting >>>>) scoped >> ??? >> >>> credentials that were registered post the CA compromise date using >>> an attestation certificate chaining up to the same intermediate CA. > > We may need to get Rolf to explain this since he wrote it. in any case, > the "policy permitting" qualifying phrase you highlight should perhaps > be "webauthn relying party local policy permitting", meaning that in > this situation, if the RP wishes to establish local policy allowing it > to not necessarily unregister scoped creds that had been reg'd > post-CA-cert-revocation, an option for them is to note that those scoped > creds (ie: {public key, cred.id<http://cred.id>, cred.type, account, attestation type, > ...}) as being of attstn type "surrogate basic" rather than "full basic". > > The connotation of "surrogate basic" being that there is > proof-of-possession of the credential private key at registration time, > but there is no proof-of-provenance of the authenticator as a whole as > there is with legit full basic attstn (or DAA, or Privacy CA). Can we create a new issue flagged as “needs discussion”? >>> >>> >>> § 5.4. Extending client processing >>> >>> >>> § >>> 5.6. Example extension >>> >>> >>> [...] >>> >>> §8. Sample scenarios >>> >> >> Why aren't these in the use cases section? > > because these are examples of RP client-side code rather than overall > context-setting use cases. Indexed Database API starts off with some beautiful examples of client-side code, and I love them for it. :) https://www.w3.org/TR/IndexedDB/#introduction >>> >>> [...] > > end
Received on Saturday, 4 June 2016 21:15:20 UTC