Skip to content

Conversation

@martinuy
Copy link
Contributor

@martinuy martinuy commented Apr 8, 2025

Hi,

I would like to request a review for the fix of JDK-8350661. In this fix, we translate the native PKCS 11 error code into an InvalidAlgorithmParameterException, as documented in the KDF::deriveKey API. With that said, different PKCS 11 libraries may throw different errors and may even (in theory) delay the error until the key is used, as SunJCE does. I believe that this is an improvement but further adjustments may be needed in the future.

No regressions observed in test/jdk/sun/security/pkcs11/KDF/TestHKDF.java.

Thanks,
Martin.-


Progress

  • Change must be properly reviewed (1 review required, with at least 1 Reviewer)
  • Change must not contain extraneous whitespace
  • Commit message must refer to an issue

Issue

  • JDK-8350661: PKCS11 HKDF throws ProviderException when requesting a 31-byte AES key (Bug - P3)

Reviewers

Reviewing

Using git

Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/24526/head:pull/24526
$ git checkout pull/24526

Update a local copy of the PR:
$ git checkout pull/24526
$ git pull https://git.openjdk.org/jdk.git pull/24526/head

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 24526

View PR using the GUI difftool:
$ git pr show -t 24526

Using diff file

Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/24526.diff

Using Webrev

Link to Webrev Comment

@bridgekeeper
Copy link

bridgekeeper bot commented Apr 8, 2025

👋 Welcome back mbalao! A progress list of the required criteria for merging this PR into master will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.

@martinuy
Copy link
Contributor Author

martinuy commented Apr 8, 2025

@franferrax @valeriepeng @wangweij may I ask you to have a look at this proposal? Thanks!

@openjdk
Copy link

openjdk bot commented Apr 8, 2025

@martinuy This change now passes all automated pre-integration checks.

ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details.

After integration, the commit message for the final commit will be:

8350661: PKCS11 HKDF throws ProviderException when requesting a 31-byte AES key

Reviewed-by: fferrari, valeriep, djelinski

You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed.

At the time when this comment was updated there had been 240 new commits pushed to the master branch:

As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details.

➡️ To integrate this PR with the above commit message to the master branch, type /integrate in a new comment.

@openjdk openjdk bot added the rfr Pull request is ready for review label Apr 8, 2025
@openjdk
Copy link

openjdk bot commented Apr 8, 2025

@martinuy The following label will be automatically applied to this pull request:

  • security

When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing list. If you would like to change these labels, use the /label pull request command.

@mlbridge
Copy link

mlbridge bot commented Apr 8, 2025

Webrevs

@wangweij
Copy link
Contributor

wangweij commented Apr 8, 2025

Looks good to me, but again I am not a PKCS #11 expert.

@djelinski
Copy link
Member

I think the usual way to handle this is by calling P11KeyGenerator.checkKeySize

KDF k = KDF.getInstance("HKDF-SHA256", p11Provider);
k.deriveKey("AES", HKDFParameterSpec.ofExtract()
.thenExpand(null, 31));
throw new Exception("No exception thrown.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this throw is unnecessary, you can just call this code straight on line 619 and remove the catch block below

 reportTestFailure(new Exception("Derivation of an AES key of " +
                    "invalid size (31 bytes) expected to throw " +
                    "InvalidAlgorithmParameterException.", e));

What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The catch block would still be needed because another exception (such as ProviderException, as is the case now) might be thrown and we want to make sure that it does not occur.

import static sun.security.pkcs11.TemplateManager.*;
import sun.security.pkcs11.wrapper.*;
import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
import static sun.security.pkcs11.wrapper.PKCS11Exception.RV.*;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: Does this import need to be a *? Wouldn't it be better to just have a import static sun.security.pkcs11.wrapper.PKCS11Exception.RV.CKR_KEY_SIZE_RANGE; ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

@martinuy
Copy link
Contributor Author

martinuy commented Apr 9, 2025

I think the usual way to handle this is by calling P11KeyGenerator.checkKeySize

We discussed calling P11KeyGenerator::checkKeySize with @franferrax but were not sure of taking this approach. We found that for DES(3) cases some fixed values are considered valid but wondered if, in theory, the PKCS 11 library can be configured to be more restrictive and reject some of them. Given that this is an error-path and should be exceptional, we thought that the cost of passing the operation to the token and handling the error was affordable. Perhaps we can do both: check beforehand and handle the error afterwards. I'll give it some more thinking.

@djelinski
Copy link
Member

Perhaps we can do both: check beforehand and handle the error afterwards.

That sounds reasonable.

Whatever you decide, I think it would be good to make sure P11HKDF, P11SecretKeyFactory and P11KeyGenerator perform the same checks during key generation.

KDF k = KDF.getInstance("HKDF-SHA256", p11Provider);
k.deriveKey("AES", HKDFParameterSpec.ofExtract()
.thenExpand(null, 31));
throw new Exception("No exception thrown.");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: "Expected InvalidAlgorithmParameterException not thrown" is clearer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This exception will be the cause, the wrapper exception will inform that InvalidAlgorithmParameterException was expected to be thrown.

Comment on lines 623 to 625
reportTestFailure(new Exception("Derivation of an AES key of " +
"invalid size (31 bytes) expected to throw " +
"InvalidAlgorithmParameterException.", e));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just use reportTestFailure(e)? I don't find the extra layer of exception too useful.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be more informative, because the original exception —not the one that we would throw if no exception is thrown— does not state which exception was expected to be thrown, is just a ProviderException which may even look good/appropriate for an invalid key size.

@valeriepeng
Copy link
Contributor

On a related note, I am working on #24393 and noticed that JSSE calls HKDF.deriveKey(...) with various names such as "TlsFinishedSecret", "TlsResumptionMasterSecret" as the key algorithm. This causes errors when using PKCS11 HKDF since the P11SecretKeyFactory.getKeyInfo() look up returns null and leads to InvalidAlgorithmParameterException. I am debating whether to do the special handling as below:

         P11SecretKeyFactory.KeyInfo ki = P11SecretKeyFactory.getKeyInfo(alg);
         if (ki == null) {
-            throw new InvalidAlgorithmParameterException("A PKCS #11 key " +
-                    "type (CKK_*) was not found for a key of the algorithm '" +
-                    alg + "'.");
+            // special handling for TLS
+            if (alg.startsWith("Tls")) {
+                ki = P11SecretKeyFactory.getKeyInfo("Generic");
+            } else {
+                throw new InvalidAlgorithmParameterException("A PKCS #11 key " +
+                        "type (CKK_*) was not found for a key of the algorithm '" +
+                        alg + "'.");
+            }

Comments or suggestions? @martinuy

@martinuy
Copy link
Contributor Author

On a related note, I am working on #24393 and noticed that JSSE calls HKDF.deriveKey(...) with various names such as "TlsFinishedSecret", "TlsResumptionMasterSecret" as the key algorithm. This causes errors when using PKCS11 HKDF since the P11SecretKeyFactory.getKeyInfo() look up returns null and leads to InvalidAlgorithmParameterException. I am debating whether to do the special handling as below:

         P11SecretKeyFactory.KeyInfo ki = P11SecretKeyFactory.getKeyInfo(alg);
         if (ki == null) {
-            throw new InvalidAlgorithmParameterException("A PKCS #11 key " +
-                    "type (CKK_*) was not found for a key of the algorithm '" +
-                    alg + "'.");
+            // special handling for TLS
+            if (alg.startsWith("Tls")) {
+                ki = P11SecretKeyFactory.getKeyInfo("Generic");
+            } else {
+                throw new InvalidAlgorithmParameterException("A PKCS #11 key " +
+                        "type (CKK_*) was not found for a key of the algorithm '" +
+                        alg + "'.");
+            }

Comments or suggestions? @martinuy

Thanks for the heads up. I was just looking at the possibility of passing key algorithms for derived keys that were not considered before but are part of the map (e.g. hmac, pbe, etc.), and wondered how that should be handled. Tls could be the opposite case: not part of the map but needs to be handled. Will give it some thinking.

I was also thinking of extending the P11SecretKeyFactory::keyInfo map to include key generation mechanisms that would facilitate querying CK_MECHANISM_INFO and obtaining valid key size ranges for each key type. This would simplify the call to P11KeyGenerator::checkKeySize from P11SecretKeyFactory::createKey and allow a call from P11HKDF. While the idea suggested by @djelinski sounds reasonable, I want to notice an implicit assumption that may not hold true for all PKCS 11 libraries: CK_MECHANISM_INFO data for mechanisms such as CKM_AES_KEY_GEN will be used for mechanisms such as CKM_HKDF_DATA/CKM_HKDF_DERIVE. P11SecretKeyFactory::createKey has a similar type of assumption.

@martinuy
Copy link
Contributor Author

@valeriepeng @djelinski @franferrax can you please take a look at this new proposal? Thanks!

@martinuy
Copy link
Contributor Author

What I have found with Tls* keys is that they are in the map but we need to translate their pseudo-mechanism to a valid one (CKK_GENERIC_SECRET). Is that enough for #24393?

Copy link
Member

@djelinski djelinski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

@openjdk openjdk bot added the ready Pull request is ready to be integrated label Apr 11, 2025
@valeriepeng
Copy link
Contributor

valeriepeng commented Apr 11, 2025

What I have found with Tls* keys is that they are in the map but we need to translate their pseudo-mechanism to a valid one (CKK_GENERIC_SECRET). Is that enough for #24393?

What I found is that there are more "TlsXXX" than those defined in P11SecretKeyFactory class which are mapped to PCKK_xxx. So, we will need to decide if those self-defined "TlsXXX" algorithms are allowed (e.g. PKCS11 will treat them as Generic secret keys or changing the TLS code to use a key algorithm recognized by PKCS11). Beside this, we need to make sure the current pseudo key type works, e.g. translating to a valid key type when necessary, as you stated.

Comment on lines 243 to 246
if (ki.keyType != CKK_GENERIC_SECRET ||
alg.equalsIgnoreCase("Generic")) {
return ki.keyType;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this check for? It's not immediately clear to me why do we look up the keyInfo using alg but then again to check to error out when the found keyType is CKK_GENERIC_SECRET && alg not equals "Generic". This seems to directly contradicts the special workaround for "TlsXXX" in my other PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the TlsXXX issue I check the pseudo-mechanism. That works if all algorithms are known to the map. I'll check how many we have and see what are the pros/cons of having them in the map. I prefer symmetric key algorithms to be in the map.

The reason for the check you referred is to block deriving keys such as HmacSHA256, PBEWithHmacSHA224AndAES_256, etc. which are not the result of HKDF derivations, but of Mac and PBE derivation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I understand it, HmacSHA256 is blocked, but not PBEWithHmacSHA224AndAES_256.

HmacSHA256

  • Has an HMACKeyInfo entry with the following non-static fields:
    • KeyInfo.algo = "HmacSHA256"
    • KeyInfo.keyType = CKK_GENERIC_SECRET
    • KeyInfo.keyGenMech = CK_UNAVAILABLE_INFORMATION
    • HMACKeyInfo.mech = CKM_SHA256_HMAC
    • HMACKeyInfo.keyLen = 256

Given ki.keyType is CKK_GENERIC_SECRET and alg is HmacSHA256, in P11HKDF::getDerivedKeyType it will enter the first case but not the if. So it will finally throw the expected exception:

InvalidAlgorithmParameterException("A key of algorithm 'HmacSHA256' is not valid for derivation.")

PBEWithHmacSHA224AndAES_256

  • Has an AESPBEKeyInfo entry with the following non-static fields:
    • KeyInfo.algo = "PBEWithHmacSHA224AndAES_256"
    • KeyInfo.keyType = CKK_AES
    • KeyInfo.keyGenMech = CK_UNAVAILABLE_INFORMATION
    • PBEKeyInfo.kdfMech = CKM_PKCS5_PBKD2
    • PBEKeyInfo.prfMech = CKP_PKCS5_PBKD2_HMAC_SHA224
    • PBEKeyInfo.keyLen = 256
    • PBEKeyInfo.extraAttrs = new CK_ATTRIBUTE[] { CK_ATTRIBUTE.ENCRYPT_TRUE }

Given ki.keyType is CKK_AES, in P11HKDF::getDerivedKeyType it will enter the first case and also the if, returning CKK_AES. Later, in P11KeyGenerator::checkKeySize(..., Token token), P11KeyGenerator::getSupportedRange will return null, because ki.keyGenMech is CK_UNAVAILABLE_INFORMATION. This will make P11KeyGenerator::checkKeySize(..., CK_MECHANISM_INFO range) enter the default case, and finally return the unmodified keySize. No exception is thrown, unless I'm missing something.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, we could save one of the if conditions by creating a separate case for CKK_GENERIC_SECRET:

        switch ((int) ki.keyType) {
            case (int) CKK_DES, (int) CKK_DES3, (int) CKK_AES, (int) CKK_RC4,
                    (int) CKK_BLOWFISH, (int) CKK_CHACHA20 -> {
                return ki.keyType;
            }
            case (int) CKK_GENERIC_SECRET -> {
                if (alg.equalsIgnoreCase("Generic")) {
                    return ki.keyType;
                }
            }
            // [...]
        }

In my view, this is quicker to understand, what do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I much prefer this idea of separate case for CKK_GENERIC_SECRET.

case (int) PCKK_TLSPREMASTER, (int) PCKK_TLSRSAPREMASTER,
(int) PCKK_TLSMASTER -> {
return CKK_GENERIC_SECRET;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's easier to troubleshoot to add a default case and not let it fall through to the exception on line 253? It's possible that P11SecretKeyFactory is enhanced with more KeyInfo, but the newly added keyType is not added here. Lumping different causes into the same exception may be harder to debug.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The exception informs the algorithm, and we know that the algorithm was found in the map because, otherwise, we would have not been able to get the KeyInfo ki received by parameter. I can add two separate exceptions if you want, but should not make much of a difference because the reason for the exception is the same: the algorithm is not valid for derivation, regardless if its underlying mechanism is CKK_GENERIC_SECRET or something else.

@martinuy
Copy link
Contributor Author

What I have found with Tls* keys is that they are in the map but we need to translate their pseudo-mechanism to a valid one (CKK_GENERIC_SECRET). Is that enough for #24393?

What I found is that there are more "TlsXXX" than those defined in P11SecretKeyFactory class which are mapped to PCKK_xxx. So, we will need to decide if those self-defined "TlsXXX" algorithms are allowed (e.g. PKCS11 will treat them as Generic secret keys or changing the TLS code to use a key algorithm recognized by PKCS11). Beside this, we need to make sure the current pseudo key type works, e.g. translating to a valid key type when necessary, as you stated.

Good, let me check this.

Copy link
Contributor

@franferrax franferrax left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @martinuy,

Thanks for your proposal, I left four comments. Two of them are suggestions/ideas, but unless my static analysis is bogus, I also found a minor bug (one comment explains the reasoning, the other suggests a low-hanging fruit test case to confirm).


putKeyInfo(new KeyInfo("TlsPremasterSecret", PCKK_TLSPREMASTER));
putKeyInfo(new KeyInfo("TlsRsaPremasterSecret", PCKK_TLSRSAPREMASTER));
putKeyInfo(new KeyInfo("TlsMasterSecret", PCKK_TLSMASTER));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you considered removing PCKK_TLSPREMASTER, PCKK_TLSRSAPREMASTER and PCKK_TLSMASTER from everywhere? We could just use CKK_GENERIC_SECRET for the TlsPremasterSecret, TlsRsaPremasterSecret and TlsMasterSecret key info entries.

Unlike PCKK_ANY, which is used for the TemplateManager to map * entries in SunPKCS11 configuration files [1], these other 3 pseudo key types are only used here in P11SecretKeyFactory. Additionally, any time these 3 pseudo key types are used, it is to map to CKK_GENERIC_SECRET.

[1] SunPKCS11 configuration files can't contain PCKK_TLS*MASTER attributes entries, only * is parsable, and corresponds with PCKK_ANY:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this idea but the downside I see is that we would need string comparison in P11KDF::getDerivedKeyType to allow TLS keys. What if we merge all PCKK_TLSPREMASTER, PCKK_TLSRSAPREMASTER and PCKK_TLSMASTER into PCKK_TLSKEY and then do the translation to CKK_GENERIC_SECRET as needed? This will also help with the new Tls* keys that I am planning to add to the map.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW, I don't like the partial "Tls" string comparison much because it's making an assumption about the algorithm name.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A new PCKK_TLSKEY pseudo key type looks good to me. Alternatively, and just thinking out loud, how about introducing a new TlsKeyInfo and using ki instanceof TlsKeyInfo in P11KDF::getDerivedKeyType?

Perhaps we could also add a new KeyInfo.supportsHKDF boolean field and store that information in the map, replacing the whole P11KDF::getDerivedKeyType call by a ki.supportsHKDF check. This would also solve the PBEWithHmacSHA224AndAES_256 case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

KeyInfo.supportsHKDF could be a valid approach. My only concern would be overloading the map/map-objects with information that is specific to each use of a key type. For example, the same criteria could be applied to store whether a key is suitable for C_CreateObject in P11SecretKeyFactory::createKey. What I liked about storing the key gen mech is that we have different users benefiting from the same information. If there is interest in exploring this idea, I can propose something.

I liked the TlsKeyInfo idea to remove PCKK_TLS* completely. We can create these keys and assign GENERIC, same as HMACKeyInfo.

"PBKDF2WithHmacSHA1",
32,
"Derivation of an invalid key algorithm");
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest adding a case with an invalid key algorithm whose key info map entry doesn't have KeyInfo.keyType=CKK_GENERIC_SECRET. For example, PBEWithHmacSHA224AndAES_256, where KeyInfo.keyType=CKK_AES.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good!

Comment on lines 243 to 246
if (ki.keyType != CKK_GENERIC_SECRET ||
alg.equalsIgnoreCase("Generic")) {
return ki.keyType;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I understand it, HmacSHA256 is blocked, but not PBEWithHmacSHA224AndAES_256.

HmacSHA256

  • Has an HMACKeyInfo entry with the following non-static fields:
    • KeyInfo.algo = "HmacSHA256"
    • KeyInfo.keyType = CKK_GENERIC_SECRET
    • KeyInfo.keyGenMech = CK_UNAVAILABLE_INFORMATION
    • HMACKeyInfo.mech = CKM_SHA256_HMAC
    • HMACKeyInfo.keyLen = 256

Given ki.keyType is CKK_GENERIC_SECRET and alg is HmacSHA256, in P11HKDF::getDerivedKeyType it will enter the first case but not the if. So it will finally throw the expected exception:

InvalidAlgorithmParameterException("A key of algorithm 'HmacSHA256' is not valid for derivation.")

PBEWithHmacSHA224AndAES_256

  • Has an AESPBEKeyInfo entry with the following non-static fields:
    • KeyInfo.algo = "PBEWithHmacSHA224AndAES_256"
    • KeyInfo.keyType = CKK_AES
    • KeyInfo.keyGenMech = CK_UNAVAILABLE_INFORMATION
    • PBEKeyInfo.kdfMech = CKM_PKCS5_PBKD2
    • PBEKeyInfo.prfMech = CKP_PKCS5_PBKD2_HMAC_SHA224
    • PBEKeyInfo.keyLen = 256
    • PBEKeyInfo.extraAttrs = new CK_ATTRIBUTE[] { CK_ATTRIBUTE.ENCRYPT_TRUE }

Given ki.keyType is CKK_AES, in P11HKDF::getDerivedKeyType it will enter the first case and also the if, returning CKK_AES. Later, in P11KeyGenerator::checkKeySize(..., Token token), P11KeyGenerator::getSupportedRange will return null, because ki.keyGenMech is CK_UNAVAILABLE_INFORMATION. This will make P11KeyGenerator::checkKeySize(..., CK_MECHANISM_INFO range) enter the default case, and finally return the unmodified keySize. No exception is thrown, unless I'm missing something.

Comment on lines 243 to 246
if (ki.keyType != CKK_GENERIC_SECRET ||
alg.equalsIgnoreCase("Generic")) {
return ki.keyType;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, we could save one of the if conditions by creating a separate case for CKK_GENERIC_SECRET:

        switch ((int) ki.keyType) {
            case (int) CKK_DES, (int) CKK_DES3, (int) CKK_AES, (int) CKK_RC4,
                    (int) CKK_BLOWFISH, (int) CKK_CHACHA20 -> {
                return ki.keyType;
            }
            case (int) CKK_GENERIC_SECRET -> {
                if (alg.equalsIgnoreCase("Generic")) {
                    return ki.keyType;
                }
            }
            // [...]
        }

In my view, this is quicker to understand, what do you think?

@martinuy
Copy link
Contributor Author

Hi @martinuy,

Thanks for your proposal, I left four comments. Two of them are suggestions/ideas, but unless my static analysis is bogus, I also found a minor bug (one comment explains the reasoning, the other suggests a low-hanging fruit test case to confirm).

Thanks for your review.

Yes, there is a hole that allows derivation for algorithms such as PBEWithHmacSHA224AndAES_256. Well spotted! I'm planning to restrict PBE algorithms based on the PBEKeyInfo subclass. Perhaps checking HMACKeyInfo doesn't hurt, even when these should not pass the mechanism check.

@openjdk openjdk bot removed the ready Pull request is ready to be integrated label Apr 17, 2025
@martinuy
Copy link
Contributor Author

I've implemented the TLSKeyInfo suggestion and added all Tls* keys to the map —after verifying they reach KDF::deriveKey in #24393.

Comment on lines 223 to 224
throw new InvalidAlgorithmParameterException("Invalid key " +
"size for algorithm '" + alg + "'.", e);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: include the requested key size in the exception message?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good!

}

static sealed class KeyInfo permits PBEKeyInfo, HMACKeyInfo, HKDFKeyInfo {
static sealed class KeyInfo permits PBEKeyInfo, HMACKeyInfo, HKDFKeyInfo,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add some comment about the purpose of KeyInfo and the PKCS11 classes which depend on it? E.g. HKDF will use the key algorithm to look up the corresponding key type. Also some comment for the various child key info classes would be nice.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

}
}

static final class TLSKeyInfo extends KeyInfo {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documenting this TLSKeyInfo is to support JSSE using HKDF to derive various keys whose algorithms are named following the "TlsXXX" convention?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

Comment on lines 590 to 605
case (int) CKK_DES, (int) CKK_DES3, (int) CKK_AES, (int) CKK_RC4,
(int) CKK_BLOWFISH, (int) CKK_CHACHA20 -> {
keyLength = P11KeyGenerator.checkKeySize(ki.keyGenMech, n,
token);
if (keyType == CKK_DES || keyType == CKK_DES3) {
fixDESParity(encoded, 0);
}
if (keyType == CKK_DES3) {
fixDESParity(encoded, 8);
if (keyLength == 112) {
keyType = CKK_DES2;
} else {
fixDESParity(encoded, 16);
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, how about separating out AES, RC4, Blowfish and ChaCha20 to a separate case? Only DES and DES3 needs parity checking and they are very legacy.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would need to repeat code if we separate (invocation to P11KeyGenerator::checkKeySize). Does not look complex enough in my opinion to merit this split.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The separation can remove 1 conditional block, so only 1 extra line and the flow looks cleaner in my opinion, e.g.

Suggested change
case (int) CKK_DES, (int) CKK_DES3, (int) CKK_AES, (int) CKK_RC4,
(int) CKK_BLOWFISH, (int) CKK_CHACHA20 -> {
keyLength = P11KeyGenerator.checkKeySize(ki.keyGenMech, n,
token);
if (keyType == CKK_DES || keyType == CKK_DES3) {
fixDESParity(encoded, 0);
}
if (keyType == CKK_DES3) {
fixDESParity(encoded, 8);
if (keyLength == 112) {
keyType = CKK_DES2;
} else {
fixDESParity(encoded, 16);
}
}
}
case (int) CKK_DES, (int) CKK_DES3 -> {
keyLength = P11KeyGenerator.checkKeySize(ki.keyGenMech, n,
token);
fixDESParity(encoded, 0);
if (keyType == CKK_DES3) {
fixDESParity(encoded, 8);
if (keyLength == 112) {
keyType = CKK_DES2;
} else {
fixDESParity(encoded, 16);
}
}
}
case (int) CKK_AES, (int) CKK_RC4, (int) CKK_BLOWFISH, (int) CKK_CHACHA20 -> {
keyLength = P11KeyGenerator.checkKeySize(ki.keyGenMech, n,
token);
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you'd still like lumping them together as you have it now, then at least move the if (keyType == CKK_DES3) block (line 624-630) to inside the previous if-block (line 621-623)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

Comment on lines +247 to +252
if (!kiClass.equals(P11SecretKeyFactory.TLSKeyInfo.class) &&
!(kiClass.equals(P11SecretKeyFactory.KeyInfo.class) &&
canDeriveKeyInfoType(ki.keyType))) {
throw new InvalidAlgorithmParameterException("A key of algorithm " +
"'" + alg + "' is not valid for derivation.");
}
Copy link
Contributor

@valeriepeng valeriepeng Apr 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, essentially, we only allow HKDF to derive keys when ki is either TLSKeyInfo or KeyInfo w/ the key types in canDeriveKeyInfoType(long) method? Are you checking each key algorithm just to rule out RC2 and IDEA? Not sure if it's worth the extra checking. Besides, if more CKK_xxx is added to KeyInfo, it'd need to be added to canDeriveKeyInfoType(long) which can be easily missed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's right: only TLSKeyInfo and selected KeyInfo keys allowed for HKDF derivation. If something is added to the map, it's not implicitly allowed. Same is true for P11SecretKeyFactory::createKey (C_CreateObject). Even P11KeyGenerator may need adjustments depending on the key type. I think that adding something should require to think what's the impact on different services, and not rely on a default/implicit behavior. I don't expect new symmetric key updates to be that frequent, though. If this becomes a problem, we can implement what @franferrax suggested and move the "uses" information to the map.

I was not thinking of RC2 or IDEA but of key algorithms that are not the result of a HKDF derivation and did not go through the checks that we would enforce otherwise. For example, a key of algorithm PBEWithHmacSHA512AndAES_256 should be the result of a PBE derivation and should have a specific key size. It would be inconsistent to obtain a key of this algorithm with a different size through a HKDF derivation. When we check or convert keys to be used in services, we rely on this information.

Copy link
Contributor

@valeriepeng valeriepeng left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updates look good to me. Thanks~

@openjdk openjdk bot added the ready Pull request is ready to be integrated label Apr 21, 2025
Copy link
Contributor

@franferrax franferrax left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the updates, it looks good to me.

@martinuy
Copy link
Contributor Author

/integrate

@openjdk
Copy link

openjdk bot commented Apr 22, 2025

Going to push as commit 5264d80.
Since your change was applied there have been 240 commits pushed to the master branch:

Your commit was automatically rebased without conflicts.

@openjdk openjdk bot added the integrated Pull request has been integrated label Apr 22, 2025
@openjdk openjdk bot closed this Apr 22, 2025
@openjdk openjdk bot removed ready Pull request is ready to be integrated rfr Pull request is ready for review labels Apr 22, 2025
@openjdk
Copy link

openjdk bot commented Apr 22, 2025

@martinuy Pushed as commit 5264d80.

💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

integrated Pull request has been integrated security [email protected]

Development

Successfully merging this pull request may close these issues.

6 participants