PSK with TLSv1.3

Matt Caswell matt at
Wed Oct 23 18:52:54 UTC 2019

On 23/10/2019 10:24, Johannes Bauer wrote:
> Hi list,
> I'm in the process of refactoring/updating code that has been using
> TLS-PSK with TLSv1.2 for a number of years successfully. I want to
> upgrade it so that it uses TLSv1.3 exclusively.
> I find it *exceptionally* hard to wrap my head around the new API and
> the documentation/manpages are extremely confusing to me.

I'd be grateful if you could submit suggested improvements as PRs to
make this clearer for the next person who comes along trying to do this.

> E.g., while
> mentions specifically TLSv1.3 clients, there's no mention of server
> implementation

The page you referenced is the man page for clients. The man page for
servers is here:

Unfortunately this page is not made any clearer by a copy & paste error
in the opening paragraph which suggests it is about the client side API
calls rather than the server side one. I've just opened a PR to fix that

> and the text does not seem to have been updated for
> TLSv1.3 entirely (e.g., there's mention of "the callback" -- clearly
> previously there was only one, now there's two,
> SSL_psk_use_session_cb_func and SSL_psk_client_cb_func).
> I'm struggling with all of it, in particular the way the server callback
> works in TLSv1.3: What I'm doing now is register a
> static int psk_server_callback(SSL *ssl, const unsigned char *identity,
> size_t identity_len, SSL_SESSION **sessptr);
> using
> SSL_CTX_set_psk_find_session_callback(cctx, psk_server_callback);
> Then, inside the callback I create a SSL_SESSION* in the callback (if I
> want to return a PSK) using SSL_SESSION_new(). Setting the master key
> (that's the PSK, right?) using SSL_SESSION_set1_master_key and the
> protocol version using SSL_SESSION_set_protocol_version is straightforward.
> But what the heck am I supposed to do with SSL_SESSION_set_cipher? The
> text in
> does not contain any hint on what to do in the server case and the text
> provided in the client manpage is exceedingly confusing:
> "Only the handshake digest associated with the ciphersuite is relevant
> for the PSK (the server may go on to negotiate any ciphersuite which is
> compatible with the digest). The application can use any TLSv1.3
> ciphersuite."
> So, wait, I can use any TLSv1.3 ciphersuite but it's not relevant
> because only the MD of the suite is used, everything else is discarded?
> So I return a cipher suite value, but it doesn't have to do anything
> with the cipher suites that I allow, it's just a "wrapper" for a MD?

Yes, exactly that. This comes about because of some quirks in the
TLSv1.3 spec. Historically (TLSv1.2 and below) the ciphersuite that is
negotiated is an important part of the session (SSL_SESSION object) and
the same ciphersuite is used in any subsequent resumption of that
session. In TLSv1.3 there is no difference between a PSK and a session.
The mechanism used for establishing a connection using a PSK is exactly
the same one used for resuming a session. So when we establish an
initial connection in TLSv1.3 we store away various values in the
session in exactly the same way that we do for TLSv1.2, and then use
some of them in the resumption. In the case of the ciphersuite its
actually only the MD part that we're interested in. With a PSK of course
there was no "initial handshake", so there was no previous ciphersuite
negotiated. However, since the mechanism is the same as for a resumption
we still need to have one there so we can get the MD out of it - which
leads to this slightly surprising way of doing things.

> Here's what I'm doing right now in my server callback:
> static int psk_server_callback(SSL *ssl, const unsigned char *identity,
> size_t identity_len, SSL_SESSION **sessptr) {
> 	fprintf(stderr, "PSK server SSL %p identity %s len %ld sess %p\n", ssl,
> identity, identity_len, *sessptr);
> 	SSL_SESSION *sess = SSL_SESSION_new();
> 	SSL_SESSION_set1_master_key(sess, (const unsigned char*)"\x00\x11\x22", 3);
> 	SSL_SESSION_set_cipher(sess, SSL_get_pending_cipher(ssl));
> 	SSL_SESSION_set_protocol_version(sess, TLS1_3_VERSION);
> 	*sessptr = sess;
> 	return 1;
> }
> All error checking omitted for now, this is obviously just a sample.
> When I try to connect to my server on the command line using s_client:
> $ openssl s_client -connect -psk_identity foo -psk 001122
> The server pukes:
> PSK server SSL 0x623000000100 identity foo len 3 sess (nil)
> 139933268309760:error:141F906E:SSL routines:tls_parse_ctos_psk:bad
> extension:../ssl/statem/extensions_srvr.c:1267:

That error is coming from this sanity check:

    if (PACKET_remaining(&binder) != hashsize) {
        goto err;

We could do with updating the error code to be something more helpful
(PRs welcome!). "Bad extension" doesn't really tell you anything. The
problem is that the callback has found the SESSION object for the
presented identity but the hash size used by the client doesn't match
the hash size associated with the ciphersuite in the SESSION object.


More information about the openssl-users mailing list