On 10/30/2018 08:36 AM, Tony Cox wrote:
Hi Folks,
Is there any interest in adding HKDF (RFC5869)? If so, I'll add
it to our next agenda.
Cheers,
-Tony
OK looking into this, I think there are some questions we need to
answer as a committee. This may be more complex than we thought.
First some background:
HKDF
HKDF (RFC 5869) has two stages which can be executed separately
or one into the other.:
- Extract - takes a salt and mixes it with the source key to
create a new key. Salt is used. If No slot is provided a salt of
zeros the length of the underlying HMAC is used.
- Expand - takes either the Export key or the original key and
mixes it with some optional Info to create a new key of
arbitrary size.
The NSS vendor specific mechanism implements 4 derive mechanism,
where the underlying HMAC is part of the mechanism (SHA1, SHA256,
SHA384, SHA512). They have the following struct:
typedef struct {
CK_BOOL bExtract;
CK_BOOL bExpand;
CK_BYTE_PTRÂ pSalt;
CK_ULONG ulSalt;
CK_BYTE_PTR pInfo;
CK_ULONG ulInfoLen;
} CK_HKDF_PARAMS;
The Salt is only used if bExtract is set to true. if No salt is
provided, a salt of all zeros the length of the underlying HMAC.
pInfo is only used if bExpand is set. It can be a zero length
string.
Either or both bExtract and bExpand can be set to true. At least
one of them must be.
The NSS vendor specific mechanism does not create any new CKK_
key types nor does it enforce any key types as input to the hkdf.
TLS usage
TLS only ever uses HKDF in either extract or expand mode, never
both in the same operation.
TLS uses HKDF expand to generate keys, IV, and key dependent
secrets passed back to the application. The info used in the
expand consists of the target object length, a label (with it's
length), and the current handshake hashes. Each TLS usage has it's
own unique label, so the actual usage could be known to the
underlying PKCS #11 module.
TLS uses extract mode to create the TLS 1.3 equivalent of the
master secret called 'the current secret':
- The current secret is generated from a previous
resumptionMasterSecret and no hash. If the
resumptionMasterSecret is NULL it's replaced with a fixed key of
all zeros, the length of the underlying HMAC function.
- A new current secret key is generated from a KEA generated key
plus a salt which is the result of a expand of the previous
current secret key with a label of "tls13 derived".
- A new current secret key is generated from a zero key (like
case 1) plus a salt which is the result of a expand of the
previous current secret key with a label of "tls13 derived".
Issues with the NSS Vendor mechanism
1. The use of multiple mechanism for each HMAC is not really
desirable. I propose we add a new CK_MECHANISM_TYPE parameter to
the CK_HMAC_PARAMS which specifies the underlying HMAC.
2. The HKDF key type should be specified. Either CKK_HKDF, or
CKK_XXXX_HMAC. I think the former is preferable, particularly if
we collapse to a single mechanism.
3. A KeyGen mechanism should be specified. There is at least one
case in NSS where we create such a key by hand by importing the
output of GetRandom!
4. There are cases in tls where the output of the HKDF (in
particular the expand output) is not used for keys, but things
like IVs. This requires the application to do the derive and then
try to export the key. In FIPS mode this does not work, tls
1.3 currently doesn't work in FIPS mode in NSS as a result. We need a way to use the HKDF
to produce just data, not a new key. Tokens which implement very
strict key management could use the label to determine if it
should allow the raw bytes to be exported to prevent an 'attacker'
from using this interface to 'extract' operating keys in the TLS
connection. The details of this interface need to be decided.
5. The salt in the extract case is a particular issue. If we use
the new interface postulated in 4. The issue is that means in the
tls extract use case 3, the user has all the information it needs
outside the token in order to generate all the additional TLS keys
since the salt if effectively the only unique thing in that
derive.
6. Need a way to specify the zero key in the derive (either as a
keyGen mechanism, or as a CK_INVALID_HANDLE).
Now, the Questions that need to be answered
- Should we use CKK_HKDF or CKK_XXXX_HMAC (I think this
determines whether we should collapse the HKDF derive to a
single or multiple mechanism)?
- If we collapse, should we specify the underlying HMAC as
CKM_XXX_HMAC or CKM_XXX mechanisms (that is the HMAC or the
underlying hash)?
- How should we provide a way to use the HKDF to get data rather
then a key?
- Add a data return field to the struct and derive a dummy key
(triggered by either a flag in the struct or a separate
mechanism).
- Use the sign interface to derive the data.
- If we do this do we need a separate struct and split pInfo
into pLabel and pData where pData is passed as the data
input in C_Sign interface.
- Do we want a separate mechanism.
- How do we handle the salt case?
- Provide a handle in the param struct to specify a key that
holds the salt.
- Just use the HKDF data interface in 2 and deal with the key
leaks
- Other?
- How do we handle the zero key?
- Add a parameter to key gen that specifies the generated key
is really a zero key.
- Accept CK_INVALID_HANDLE in C_Derive and define that to mean
use a zero key internally generated.
- Create a new Mechanism that specifically generates aÂ
CKK_HKDF key with zeros.
After looking at this, my current thinking is:
- CKK_HKDF
- CKM_XXXX (underlying hash, but I'm mostly agnostic here,
though).
- B - I rather dislike the 'fake key' interface, though we do
have it in the old SSL key derive case.
- yes - the alternative is we always pass a zero data to
C_Sign, which is doable, just weird to me.
- yes - this changes for me if we decide not to go with a
separate struct.
- A - I think this simple change will allow complete tls key
separate for those tokens that want to implement that feature.
It would also match the existing internal NSS ssl api.
- B - I think A could be dangerous. If someone tries to generate
a key and gets the parameters wrong they would wind up with a
zero key. C is a real option for me as well. B means the
application doesn't have to look up the underlying HMAC key
length, the token would do it.