Is that safe?
Safe is something that applies to systems, not algorithm use alone.
Is it a correct way to use HKDF?
That seems to be the case yes.
the HKDF RFC specifies the Info
parameter as a string, by which they mean an octet string. Currently you've specified a number value. However, you should also specify a way to convert that to an octet string.
You haven't specified the info
encoding but that's not hard, you could e.g. use "client" | 64 bit client number
for client keys and "session" | 64 bit client number
for the session keys. The labels "client"
and "session"
can be encoded using ASCII. The number could be encoded as a 64 bit unsigned value in network order.
Quite often we use multiple session keys, e.g. one for sending and one for receiving, and possibly one for MAC generation or key confirmation (validating that both sides have the expected key without having to send a particular message, e.g. a MAC over a session transcript). You could add some context for that as well, e.g. "session_send"
and "session_receive"
(etc.) instead of the current labels.
The size should obviously be linked to the possible number of clients and / or sessions. That requires protection against an adversary being able to deliberately increase the session counter though; a 64 bit or larger value would require fewer assumptions about security.
So although not incorrect, I would not deem it complete.
I am concatenating two stages of expansion to generate session keys.
That's fine in general.
For a system it could make more sense to use e.g. double ratcheting as to provide forward security in addition to dependence of keying material.