Change:
This was already permitted by the transport document, but it was not adequately explained. The example that tripped us up was ALPN, which has received a bit of attention here.
Sec 8.1. of the quic-tls draft states that, if an application protocol is not negotiated, endpoints MUST immediately terminate the connection with a noapplicationprotocol TLS alert (QUIC error code 0x178). For additional security, some TLS implementations choose to hide the exact reason for a handshake failure, and only issue a handshakefailure alert instead. The QUIC layer obtaining the "true alert" may be difficult or impossible. Maybe the requirement to send noapplication_protocol should be a SHOULD instead.
This is probably fine as the reason is observable. It is true that some TLS alerts are hidden, but that is generally because that leaks private information. But here the information is public, or at least shared by client and server. Is the concern that this is potentially sent in an Initial packet? Because that shouldn't be necessary, or even possible.
I'm not concerned with the security implications, or lack thereof, of sending noapplicationprotocol. My main concern is that we, and other implementations, will be non-compliant with the draft because we have no choice but to send a general handshakefailure alert in this case. Our TLS implementation doesn't allow us to choose which alerts are converted to handshakefailure before being reported to QUIC; it's all or nothing.
I'm not sure that I'm following this: you are sending a QUIC CONNECTIONCLOSE frame, not a TLS alert. Our stack makes the appropriate CONNECTIONCLOSE directly without talking to TLS.
I'm referring to the error code field in the QUIC CONNECTIONCLOSE frame. In the case of a crypto error, the QUIC error code for handshake failure is 0x128. The QUIC error code for no application protocol is 0x178. The draft mandates that we send 0x178. In our stack, TLS does the ALPN parsing and if there is a TLS alert, QUIC converts it to a CONNECTIONCLOSE with the reported error code; TLS alert 0x28 for handshakefailure, 0x78 for noapplicationprotocol. Due to TLS converting all alerts during the handshake to handshakefailure, we end up sending a QUIC CONNECTION_CLOSE frame with 0x128, instead of 0x178.
I believe what you are asking is already catered for. The requirement for the failure case is MUST close, and the most appropriate error code is defined.
To follow up with a way to resolve this. I don't believe we need to change any normative language and so this is not a design issue. I'll leave it to editors' discretion on whether there is any editorial work required to address the original concern.
I'm still stuck on how the TLS code needs to be involved in this. Our code generates the alert, but does so .
NAME I think the problem is due to how certain implementations handle ALPN. In case of H2O, we handle ALPN in our onclienthello callback, that is invoked by picotls (URL). At this point, the call stack is H2O (application) -> quicly (QUIC stack) -> picotls (TLS stack) -> onclienthello (application callback). In this type of design, if the TLS stack is going to determine the error code (or change the error code returned from a callback), then it would be impossible (or difficult) for the QUIC stack to send the appropriate code.
I see. I hope that the application isn't the callback code, but that quicly is involved in this somehow; otherwise, you have just made H2O responsible for fulfilling QUIC requirements that you might assume quicly would be responsible for. In general, I would assume that you have access to QUIC transport state in your callback from the TLS stack. Let's say that you choke on the ALPN. You cause TLS to issue a generic alert (which is fine, as Lucas points out). At some later point that alert is going to hit QUIC code again so that it can be translated to a QUIC alert. If you can save the reason from the callback (presumably you need this capability to save transport parameters) then you can construct the right message. Of course, that's complicated. The generic alert is fine.
FWIW this particular error code is the one I've seen the most of during interop. So I will make the observation that anyone building a stack that causes an ALPN failure to not be exposed, is going to cause a lot of problems for future QUIC as a generic transport layer.
NAME Yeah I think my preference goes to not having an abstraction layer in our QUIC protocol stack for hiding TLS handshake protocol API. I'd argue that the intent of using TLS in QUIC is to reuse TLS including its extensions when possible. Requiring (or recommending) a QUIC stack to provide abstraction for those extensions (that happen at the handshake protocol level) reduces that benefit. That said, I think we agree that in this particular case a generic alert is fine, and that what we need at most is an editorial improvement.
I agree that the normative requirements are fine. It would be good to expose the specific ALPN error, and we can make the necessary TLS change to detect this properly. However, "generic TLS alerts" are a user-configurable setting, and we're reluctant to just override customer preference just because it's in a CONNECTION_CLOSE instead of a TLS Alert. Moreover, when the TLS WG gets around to encrypting the ALPN, it is not clear that exposing this error is so benign anymore.

Moreover, when the TLS WG gets around to encrypting the ALPN, it is not clear that exposing this error is so benign anymore. Will ESNI/ECHO define a way to protect the confidentiality of such TLS alerts?
Not with the current designs, but we've been discussing encrypting more, in which case maybe?

Discussion:
If TLS experiences an error, it generates an appropriate alert as defined in Section 6 of TLS13. <del> A TLS alert is turned into a QUIC connection error by converting the one-byte alert description into a QUIC error code.  The alert </del> <ins> A TLS alert is converted into a QUIC connection error.  The alert </ins> description is added to 0x100 to produce a QUIC error code from the range reserved for CRYPTO_ERROR.  The resulting value is sent in a QUIC CONNECTION_CLOSE frame of type 0x1c.

The alert level of all TLS alerts is "fatal"; a TLS stack MUST NOT generate alerts at the "warning" level. <ins> QUIC permits the use of a generic code in place of a specific error code; see Section 11 of QUIC-TRANSPORT.  For TLS alerts, this includes replacing any alert with a generic alert, such as handshake_failure (0x128 in QUIC).  Endpoints MAY use a generic error code to avoid possibly exposing confidential information. </ins> 4.11. After QUIC moves to a new encryption level, packet protection keys

protocol negotiation.  TLS uses Application Layer Protocol Negotiation (ALPN) ALPN to select an application protocol.  Unless another mechanism is used for agreeing on an application protocol, <del> endpoints MUST use ALPN for this purpose.  When using ALPN, endpoints MUST immediately close a connection (see Section 10.3 in QUIC- TRANSPORT) if an application protocol is not negotiated with a no_application_protocol TLS alert (QUIC error code 0x178, see tls- errors).  While ALPN only specifies that servers use this alert, QUIC clients MUST also use it to terminate a connection when ALPN negotiation fails. </del> <ins> endpoints MUST use ALPN for this purpose. When using ALPN, endpoints MUST immediately close a connection (see Section 10.3 of QUIC-TRANSPORT) with a no_application_protocol TLS alert (QUIC error code 0x178; see tls-errors) if an application protocol is not negotiated.  While ALPN only specifies that servers use this alert, QUIC clients MUST use error 0x178 to terminate a connection when ALPN negotiation fails. </ins> An application protocol MAY restrict the QUIC versions that it can operate over.  Servers MUST select an application protocol compatible