GREASE'd ALPN values - a RFC 8701 / RFC 7301 / JEP 244 discussion

Alexander Scheel ascheel at redhat.com
Thu Oct 8 16:20:23 UTC 2020


\o Hi all,

I saw that ALPN support from JEP 244 was backported to JDK8 and I've
recently had the time to take a closer look at it. For context, I'm
one of the maintainers of JSS, a NSS wrapper for Java. I've been
discussing this with another contributor, Fraser (cc'd).

One of the concerns we have with the implementation (and its exposure
in the corresponding SSLEngine/SSLSocket/SSLParameters interface) is
that protocols are passed in as Strings. However, RFC 7301 says in
section 6:

>    o  Identification Sequence: The precise set of octet values that
>       identifies the protocol.  This could be the UTF-8 encoding
>       [RFC3629] of the protocol name.

When applied with GREASE'd values from RFC 8701, Strings don't work
well. In particular, most of the registered values [0] are non-UTF-8,
which can't be easily round-tripped in Java. This means that while
precise octet values are specified by IANA, they cannot be properly
specified in Java.

In particular:

    byte[] desired = new byte[]{ (byte) 0xFA, (byte) 0xFA };
    String encoded = new String(desired, StandardCharsets.UTF_8);
    byte[] wire    = encoded.getBytes(StandardCharsets.UTF_8);
    String round   = new String(wire, StandardCharsets.UTF_8);

fails, as does choosing US_ASCII for the encoding:

    byte[] desired = new byte[]{ (byte) 0xFA, (byte) 0xFA };
    String encoded = new String(desired, StandardCharsets.US_ASCII);
    byte[] wire    = encoded.getBytes(StandardCharsets.UTF_8);
    String round   = new String(wire, StandardCharsets.UTF_8);

Note that we (at the application level) can't control the final (wire
/ round-tripped) encoding to UTF_8 as this is done within the SunJSSE
implementation:

https://github.com/openjdk/jdk11u-dev/blob/master/src/java.base/share/classes/sun/security/ssl/AlpnExtension.java#L100
https://github.com/openjdk/jdk11u-dev/blob/master/src/java.base/share/classes/sun/security/ssl/AlpnExtension.java#L170
https://github.com/openjdk/jdk11u-dev/blob/master/src/java.base/share/classes/sun/security/ssl/AlpnExtension.java#L223
https://github.com/openjdk/jdk11u-dev/blob/master/src/java.base/share/classes/sun/security/ssl/AlpnExtension.java#L425

and perhaps other files I'm missing.


This decreases interoperability with other TLS implementations.
OpenSSL [1], NSS [2], and GnuTLS [3] support setting opaque blobs as
the ALPN protocol list, meaning the caller is free to supply GREASE'd
values. Go on the other hand still uses its string [4], but that
string class supports round-tripping non-UTF8 values correctly [5].

Additionally, it means that GREASE'd values sent by Java applications
aren't compliant with the RFC 8701/IANA wire values.


Is there some workaround I'm missing?

I believe that setting US_ASCII internally in SunJSSE isn't sufficient
to ensure the right wire encoding gets used. I'm thinking the only
real fix is to deprecate the String methods and provide byte[] methods
for all identifiers.


Thanks in advanced,

Alex

[0]: https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
[1]: https://www.openssl.org/docs/man1.1.0/man3/SSL_CTX_set_alpn_protos.html
[2]: https://github.com/nss-dev/nss/blob/master/lib/ssl/ssl.h#L392-L409
[3]: https://gnutls.org/manual/html_node/Application-Layer-Protocol-Negotiation-_0028ALPN_0029.html
[4]: https://golang.org/pkg/crypto/tls/#ClientHelloInfo
[5]: https://play.golang.org/p/PjyZ-NZmKQe




More information about the security-dev mailing list