ssl: Bugfix: Use cypher suite's PRF in prf/5.

Kenneth Lakin <[email protected]> Tue, 3 May 2016 00:37:40 -0700
Newsgroups gmane.comp.lang.erlang.patches
Message-ID <[email protected]>
This is an OpenPGP/MIME signed message (RFC 4880 and 3156)
--===============9086908550973382217==
Content-Type: multipart/signed; micalg=pgp-sha256;
 protocol="application/pgp-signature";
 boundary="NxPhSUeDWR4msNkCmb1uBFxkb22LmhT26"

This is an OpenPGP/MIME signed message (RFC 4880 and 3156)
--NxPhSUeDWR4msNkCmb1uBFxkb22LmhT26
Content-Type: multipart/mixed; boundary="XHBuCPPvVBSxoPhVO86g7b2ro3ISJ5qWh"
From: Kenneth Lakin <[email protected]>
To: [email protected]
Message-ID: <[email protected]>
Subject: ssl: Bugfix: Use cypher suite's PRF in prf/5.

--XHBuCPPvVBSxoPhVO86g7b2ro3ISJ5qWh
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable



The PR is at:
https://github.com/erlang/otp/pull/1042

You should be able to fetch with:
git fetch git://github.com/kennethlakin/otp.git tls-use-negotiated-prf

And compare with:
https://github.com/erlang/otp/compare/maint-18...kennethlakin:tls-use-neg=
otiated-prf
https://github.com/erlang/otp/compare/maint-18...kennethlakin:tls-use-neg=
otiated-prf.patch

The remainder of this message is the same as the text in the PR:

TLS 1.2 allows the negotiated cipher to specify its own PRF algorithm.
ssl:prf/5 currently follows a code path that uses hard-coded PRF
algorithms that are always correct for TLS 1.0, always wrong for TLS
1.1, and sometimes correct for TLS 1.2. The TLS handshaking code calls
tls_v1:prf/5 through another path, so it does not run into this bug.
(Given that ssl:prf/5 was added to support EAP-TLS and friends, I'm
probably one of a tiny handful of people who make use of this function.
(EAP-TLS and friends use the output of the TLS PRF as keying material.
If the authenticating server's keys don't match what the client thinks
the keys should be, then the client bails out.))

The patch modifies ssl_handshake:prf to accept a PRF algorithm (rather
than using the hard-coded one), and
ssl_connection:handle_sync_event({prf ...) to pass the connection's
negotiated PRF algorithm along to ssl_handshake:prf. It also adds code
to test the output of ssl:prf/5.

To demonstrate the bug and the correctness of the fix, run the following
in an Erlang shell:

dbg:start().
dbg:tracer().
dbg:tpl(tls_v1, prf, []).
dbg:p(all, c).
ssl:start().
Ciphers=3D{ciphers, [{ecdhe_rsa,aes_256_gcm,null,sha384},
{ecdhe_rsa,aes_256_cbc,sha384,sha384}]}.
{ok, S}=3Dssl:connect("www.google.com", 443, [Ciphers]).
ssl:prf(S, <<"">>, <<"">>, [<<"">>], 16).
ssl:close(S).
f(S).
f(Ciphers).

(Notice that we're only using ciphers that have a SHA384 PRF.) The buggy
code produces the following output. (Random binary data has been elided.
The PRF to be used is the first argument to tls_v1:prf. 5 is the SHA384
PRF, 4 is the SHA256 PRF, and both values are defined in ssl_record.hrl.)=


7> {ok, S}=3Dssl:connect("www.google.com", 443, [Ciphers]).
(<0.59.0>) call tls_v1:prf(5,...,<<"master secret">>,[...,...],48)
(<0.59.0>) call tls_v1:prf(5,...,"key expansion",[...,...],72)
(<0.59.0>) call tls_v1:prf(5,...,"key expansion",[...,...],72)
(<0.59.0>) call tls_v1:prf(5,...,<<"client finished">>,...,12)
(<0.59.0>) call tls_v1:prf(5,...,<<"server finished">>,...,12)
{ok,{sslsocket,{gen_tcp,#Port<0.892>,tls_connection,
                        undefined},
               <0.59.0>}}
8> ssl:prf(S, <<"">>, <<"">>, [<<"">>], 16).
(<0.59.0>) call tls_v1:prf(4,<<>>,<<>>,[<<>>],16)
{ok,<<166,249,145,171,43,95,158,232,6,60,17,90,183,180,0,
      155>>}

Notice how the first argument passed to tls_v1:prf/5 by way of the call
to ssl:prf/5 is *not* the same as the first argument passed in during
the handshaking process.

The fixed code produces the following output:

7> {ok, S}=3Dssl:connect("www.google.com", 443, [Ciphers]).
(<0.59.0>) call tls_v1:prf(5,...,<<"master secret">>,[...,...],48)
(<0.59.0>) call tls_v1:prf(5,...,"key expansion",[...,...],72)
(<0.59.0>) call tls_v1:prf(5,...,"key expansion",[...,...],72)
(<0.59.0>) call tls_v1:prf(5,...,<<"client finished">>,...,12)
(<0.59.0>) call tls_v1:prf(5,...,<<"server finished">>,...,12)
{ok,{sslsocket,{gen_tcp,#Port<0.892>,tls_connection,
                        undefined},
               <0.59.0>}}
8> ssl:prf(S, <<"">>, <<"">>, [<<"">>], 16).
(<0.59.0>) call tls_v1:prf(5,<<>>,<<>>,[<<>>],16)
{ok,<<153,182,217,96,186,130,105,85,65,103,123,247,146,
      91,47,106>>}

Notice how the first argument passed to tls_v1:prf/5 by way of the call
to ssl:prf/5 is the now same as the first argument passed in during the
handshaking process.



--XHBuCPPvVBSxoPhVO86g7b2ro3ISJ5qWh--

--NxPhSUeDWR4msNkCmb1uBFxkb22LmhT26
Content-Type: application/pgp-signature; name="signature.asc"
Content-Description: OpenPGP digital signature
Content-Disposition: attachment; filename="signature.asc"

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2

iQIcBAEBCAAGBQJXKFVEAAoJEDpEx4ha9HUKPKkP/jjeaGWn3C/FWc80JAkJHXle
SjcuByIp+jvN7hbyNiR+gfFUF5wa5QmhuxesdFs4k7cSiryK3rRNLh21UwPXwoS8
PKbAU9ned0iamdkPx/ltQ9zfdSeI0maGvgPkovfvI9FlT9hNjF7jdkRNIw8RjeER
iNctkC+kk8wunM0sH2rA34uUy4kUrIno7UmgVtof6Yk8wEZGWiaT0pH4NrH82Lwt
4SQ/NXTMMdhclUv0yJG6dZj8QJ8iX/BqGxftvzDBG1o1kuVAquvKwKy34fXckM9j
qxzKTofCKZd2UzBbuouGCxQwGf3X/jM2SPAJt8fmn8dIxThsbPtaxoE16LIUOlry
yjv8t0fE9CTqF2C1rOcGImJDz/pAwnSYo1BsYdBMeT7ZjUrywqjSy0ZLH4sZz5JP
iKjPvxCmfkDeGch+WBsTLOiQxoCcfcdUjoFXIob4cNMxnQSHdpZDYWJm+R10ucm8
hqfqLiF828LN9gRgUrOdeJ/5ukByhD6HuOcpn8eCwCyl/bCY5u73NsbTUqWx73HU
DhB8KAW0xQGWcUwaITTSnjhBLT5iuwM/ZFZ4TKPldqSlnxumn59z0EMvzkU3TcMZ
QmMDnFFYEb/Qq75GglOUhqSey/3FZ+owCXI3bCuIANIVWhWP/uZedhEc7RKGzTjd
JuD9knssIo+6hwb3UPZ7
=oqrQ
-----END PGP SIGNATURE-----

--NxPhSUeDWR4msNkCmb1uBFxkb22LmhT26--

--===============9086908550973382217==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

_______________________________________________
erlang-patches mailing list
[email protected]
http://erlang.org/mailman/listinfo/erlang-patches

--===============9086908550973382217==--