NSS v3.52 and TLSv1.3's Post-Handshake Authentication -- completion notification?

16 views
Skip to first unread message

Alex Scheel

unread,
Jul 6, 2020, 1:56:28 PM7/6/20
to dev-tec...@lists.mozilla.org
Greetings folks,

With moz-bz#1561637 having landed upstream (and subsequently v3.52 being
shipped downstream in Fedora), I've been testing JSS's SSLEngine with
TLSv1.3 again.

One of the issues I've had is there's no good indicator for either
key-exchange or post-handshake auth being started or completed by either
party (client or server).

SSLEngine (in Java) for those unaware has (roughly) the following semantics:

- After beginHandshake() has been called, handshake status stays in
NEED_WRAP, NEED_UNWRAP, or NEED_TASK until the handshake is
complete. Then it moves to FINISHED and finally to
NOT_HANDSHAKING.
- beginHandshake() is called implicitly the first time from wrap()
and unwrap().
- If certs are required after the initial handshake (such as via PHA in
TLSv1.3 or a full re-handshake when available in TLSv1.2 and earlier),
beginHandshake() semantics apply again.


The last requirement is the "hard" one under TLSv1.3.

I've set a SSL_HandshakeCallback() that informs me when the initial
handshake is completed. This works fine for both client and
server-initiated re-handshakes under TLSv1.2. However, this callback
is never executed under TLSv1.3 (in part) because PHA and re-key requests
aren't strictly a new handshake.

So what I ask are:

- Is there a reliable way to tell (as both a client and server) when
PHA/rekey requests have started?

- Is there a reliable way to tell (as both a client and server) when
PHA/rekey requests have completed?

- Is there a reliable way to tell (as both a client and server) when
a handshake has been initiated by the other party (on TLS < 1.2)?


I'm fine living without 1 and 3, but 2 is the more of an issue for me.

I'd be happy to submit the code changes to trigger SSL_HandshakeCallback
via TLSv1.3 PHA/re-key requests -- but I'm not sure if that'd technically
conform to the function's documentation:

> /*
> ** Set the callback that gets called when a TLS handshake is complete. The
> ** handshake callback is called after verifying the peer's Finished message and
> ** before processing incoming application data.
> **
> ** For the initial handshake: If the handshake false started (see
> ** SSL_ENABLE_FALSE_START), then application data may already have been sent
> ** before the handshake callback is called. If we did not false start then the
> ** callback will get called before any application data is sent.
> */
> typedef void(PR_CALLBACK *SSLHandshakeCallback)(PRFileDesc *fd,
> void *client_data);
> SSL_IMPORT SECStatus SSL_HandshakeCallback(PRFileDesc *fd,
> SSLHandshakeCallback cb, void *client_data);

It might be surprising behavior? But on the other hand, these two do
kinda replace the (insecure) second handshake. Perhaps gating it behind
an option flag would suffice though (SSL_HANDSHAKE_CALLBACK_FOR_PHA or
some such)?

When looking at the other SSL_SendCertificateRequest experimental API
docs, it does say AuthCertificateComplete is called on the server side:

> /* This function allows a server application to trigger
> * re-authentication (TLS 1.3 only) after handshake.
> *
> * This function will cause a CertificateRequest message to be sent by
> * a server. This can be called once at a time, and is not allowed
> * until an answer is received.
> *
> * This function is not allowed for use with DTLS or when external
> * PSK authentication has been negotiated. SECFailure is returned
> * in both cases.
> *
> * The AuthCertificateCallback is called when the answer is received.
> * If the answer is accepted by the server, the value returned by
> * SSL_PeerCertificate() is replaced. If you need to remember all the
> * certificates, you will need to call SSL_PeerCertificate() and save
> * what you get before calling this.
> *
> * If the AuthCertificateCallback returns SECFailure, the connection
> * is aborted.
> */
> #define SSL_SendCertificateRequest(fd) \
> SSL_EXPERIMENTAL_API("SSL_SendCertificateRequest", \
> (PRFileDesc * _fd), \
> (fd))


But this doesn't help on the client side. Additionally, my situation with
AuthCertificateCallback is... complicated (it is either not explicitly set
to use the default NSS value, set to something with no knowledge of my
SSLEngine, or something with knowledge of the SSLEngine) -- so I'm not
sure I like that design.



Thoughts? I feel like I might be missing something.

- Alex

Martin Thomson

unread,
Jul 6, 2020, 9:46:54 PM7/6/20
to mozilla's crypto code discussion list, Daiki Ueno
Daiki might have some ideas about how to approach this.

I think that we considered this when we first landed this code, but
deferred adding any callbacks until it was clear what the right answer
was. As you say, you get the callback, but you might not if the
request is rejected.
> --
> dev-tech-crypto mailing list
> dev-tec...@lists.mozilla.org
> https://lists.mozilla.org/listinfo/dev-tech-crypto

Martin Thomson

unread,
Jul 7, 2020, 10:26:29 PM7/7/20
to Daiki Ueno, mozilla's crypto code discussion list
On Wed, Jul 8, 2020 at 2:36 AM Daiki Ueno <du...@redhat.com> wrote:
>
> Martin Thomson <m...@mozilla.com> writes:
>
> > I think that we considered this when we first landed this code, but
> > deferred adding any callbacks until it was clear what the right answer
> > was. As you say, you get the callback, but you might not if the
> > request is rejected.
>
> I think that is about the server side. On the client side, it seems
> even harder to detect the completion, because there will be no
> indication of acceptance until the next application data is sent.

Fair point, but we have never had a way to indicate that you consider
your peer to be authenticated as "X". It's down to individual
authorization decisions to reflect whatever opinion you have formed.
For instance, a 403 response in HTTP might provide some more
information.

Alex Scheel

unread,
Jul 8, 2020, 9:05:33 AM7/8/20
to mozilla's crypto code discussion list, Daiki Ueno
Thanks Martin and Daiki!
(I seem to have lost Daiki's reply)

I don't care too much (from a client perspective) whether or not our
credentials were accepted. If they weren't, the worst that will happen
is I'll get an alert later and everything will close down anyways -- or,
the server will ignore them (passing the failure to the application)
and I'm left wondering why it asked for credentials anyways. Perhaps the
application will reject the lack of credentials and close the connection
itself, but with a pretty application-level error message.

Either way, my working assumption is that if the server doesn't like the
credentials, it'll at worst close the connection. At best, it'll prompt
me for new credentials again. (It'll probably get the same answer, but
that's a different discussion). "meh"?

So on the client side, I'd be fine triggering the alert once the outbound
FINISHED message is written to the wire.


The same holds on the server: I'd always trigger once the
AuthCertificateCallback is complete, regardless of its outcome (accepted
or not). I've not checked NSS's behavior here: if PHA is requested, and
the cert fails to verify, does NSS return the error as a fatal alert? Or
is it up to the application code whether a failed PHA gives a fatal alert?
The doc text for SendCertificateRequest seems to indicate that no fatal
alert is sent in the event of invalid certificates, which is fine by me.

I think that this last suggestion differs from the triggering during
regular handshakes? If the handshake fails, the handshake callback won't
trigger... We failed to get new, valid credentials, but--if the application
needs to know that we negotiated new credentials, it can always save the
previous PeerCertificate() prior to executing the PHA request and then
check it in the callback and act accordingly.


My 2c,

- Alex

Daiki Ueno

unread,
Jul 13, 2020, 11:05:29 AM7/13/20
to Martin Thomson, mozilla's crypto code discussion list
Martin Thomson <m...@mozilla.com> writes:

> I think that we considered this when we first landed this code, but
> deferred adding any callbacks until it was clear what the right answer
> was. As you say, you get the callback, but you might not if the
> request is rejected.

I think that is about the server side. On the client side, it seems
even harder to detect the completion, because there will be no
indication of acceptance until the next application data is sent.

Regards,
Reply all
Reply to author
Forward
0 new messages