[#1081] Wrap with a 2048-bit key in the key wrapping check, and report a provider refusing with an Error - #1104
Conversation
maximthomas
left a comment
There was a problem hiding this comment.
praise: The change sits where #1081 fails and removes both causes the check had.
CryptoManagerImpl.isConfigurationChangeAcceptable(CryptoManagerImpl.java:409-431) wraps with a 2048-bit RSA key (SPKI headerMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA, SHA256withRSA) and the constant key idkey-wrapping-check. The check no longer needs a 1024-bit wrap or MD5.inApprovedOnlyBcFipsThread(CryptoManagerTestCase.java:464-474) sets approved-only mode per thread withCryptoServicesRegistrar.setApprovedOnlyMode(true), so the in-core server never runs in approved-only mode.
question (non-blocking): Should a LinkageError from a broken provider jar become a configuration refusal, now that the refusal drops its cause?
opendj-server-legacy/src/main/java/org/opends/server/crypto/CryptoManagerImpl.java:354, :377, :433, :451-457, :297
The widened catches take every Error except VirtualMachineError. That includes ExceptionInInitializerError and NoClassDefFoundError. Take a provider ahead of SunJCE whose KeyGeneratorSpi has a failing static initializer. It throws an ExceptionInInitializerError with a null message. getExceptionMessage does not unwrap that error. It prints only the class name and the first frame, so the reason reads ExceptionInInitializerError(<File>:<line>). Line 297 then throws new InitializationException(why.get(0)) with no cause. The stack trace goes to logger.traceException only, and the shipped configuration disables the debug logger. At the base, the same Error escaped uncaught, and the JVM printed the full stack with Caused by. The start fails in both cases, so only the diagnosis is lost. If this is not intended, it is a Minor, and the fix below restores the base behaviour for a broken jar. The BC-FIPS refusals are still reported, because FipsUnapprovedOperationError extends AssertionError.
private static void rethrowIfVirtualMachineError(final Throwable t)
{
if (t instanceof VirtualMachineError || t instanceof LinkageError)
{
throw (Error) t;
}
}suggestion (non-blocking): No test pins rethrowIfVirtualMachineError.
opendj-server-legacy/src/main/java/org/opends/server/crypto/CryptoManagerImpl.java:355, :378, :434, :451-457
Every new case raises BC-FIPS's FipsUnapprovedOperationError, which the helper lets through. No case in CryptoManagerTestCase makes a provider throw a VirtualMachineError. Deleting any of the three calls, or emptying the helper's body, therefore leaves all 36 cases green. With that mutant, an OutOfMemoryError or StackOverflowError from a provider becomes an ERR_CRYPTOMGR_* refusal instead of propagating.
/** A VirtualMachineError from a provider is not a refusal of the configuration: it propagates. */
@Test
public void testVirtualMachineErrorFromAProviderPropagates() throws Exception
{
final CryptoManagerImpl cm = DirectoryServer.getCryptoManager();
final CryptoManagerCfg cfg = getServerContext().getRootConfig().getCryptoManager();
final Provider vme = new Provider("VmeProbe", "1.0", "KeyGenerator failing like the JVM") {};
vme.put("KeyGenerator.HmacVmeProbe", StackOverflowingKeyGenerator.class.getName());
Security.insertProviderAt(vme, 1);
try
{
assertThatThrownBy(() -> cm.isConfigurationChangeAcceptable(
withProperty(cfg, "getMacAlgorithm", "HmacVmeProbe"), new ArrayList<LocalizableMessage>()))
.isInstanceOf(StackOverflowError.class);
}
finally
{
Security.removeProvider("VmeProbe");
}
}
/** A key generator which fails the way only the JVM fails. */
public static final class StackOverflowingKeyGenerator extends javax.crypto.KeyGeneratorSpi
{
@Override
protected void engineInit(java.security.SecureRandom random) {}
@Override
protected void engineInit(java.security.spec.AlgorithmParameterSpec params, java.security.SecureRandom random) {}
@Override
protected void engineInit(int keySize, java.security.SecureRandom random) {}
@Override
protected javax.crypto.SecretKey engineGenerateKey()
{
throw new StackOverflowError("provider probe");
}
}Pin: this case fails if the call at :378 is deleted or the helper's body is emptied. A cipher-arm twin, with a KeyGenerator for the transformation's algorithm, catches the deletion at :355. OpenJDK does not require a signed JCE provider.
…ing check, and report a provider refusing with an Error The crypto manager checks its key wrapping transformation at every start by wrapping a MAC key with a hard-coded certificate. Its 1024-bit RSA key is refused by BC-FIPS in approved-only mode with FipsUnapprovedOperationError, which escaped catch (Exception) and kept the server from starting. - Use a 2048-bit RSA certificate (SHA256withRSA) for the check. - Use a constant key identifier there instead of an MD5 digest of it. - Report an Error from a provider during the cipher, MAC and key wrapping checks as a refusal of the configuration; rethrow VirtualMachineError. Fixes OpenIdentityPlatform#1081
… well, and pin the rethrow Review round 1 of OpenIdentityPlatform#1104. - A LinkageError from a broken provider jar (ExceptionInInitializerError, NoClassDefFoundError) is not a refusal either: reported as one, it lost its cause, since a refusal carries only a message. The helper, renamed rethrowIfNotARefusal, rethrows it along with VirtualMachineError. The BC-FIPS refusals extend AssertionError and stay refusals. - testErrorWhichIsNotARefusalPropagates fails the cipher, MAC and key wrapping checks through a probe provider with a StackOverflowError and an ExceptionInInitializerError, and expects each to propagate.
77ceaed to
cf821bd
Compare
|
Both points taken; round 2 is cf821bd, rebased on the current question (LinkageError): not intended. The helper, renamed suggestion (no pin): Mutants,
Without mutants, 42/42 pass. The PR description is updated. |
Fixes #1081
Problem
The crypto manager checks its key wrapping transformation at every start (and on every change of
key-wrapping-transformation) by wrapping a freshly generated MAC key with the public key of a hard-coded certificate. That certificate carries a 1024-bit RSA key. The BC-FIPS provider in approved-only mode refuses RSA keys under 2048 bits withFipsUnapprovedOperationError, anError, which escapedcatch (Exception)and kept the server from starting.Change
CryptoManagerImpl.isConfigurationChangeAcceptable:getInstanceKeyID). The wrapped key is thrown away, so the identifier only has to be a string, and a restricted runtime may not offer MD5.Exception | Error, so a provider refusing with anErroris reported as a refusal of the configuration (ERR_CRYPTOMGR_CANNOT_GET_REQUESTED_ENCRYPTION_CIPHER,..._MAC_ENGINE,..._PREFERRED_KEY_WRAPPING_CIPHER).VirtualMachineErrorandLinkageErrorare rethrown, as they were before: neither is a refusal, and aLinkageErrorfrom a broken provider jar (ExceptionInInitializerError,NoClassDefFoundError) reported as a refusal would lose its cause, since a refusal carries only a message and the start then fails with anInitializationExceptionwithout a cause. BC-FIPS refuses with subclasses ofAssertionError, which stay refusals. BC-FIPS in approved-only mode also refuses to generate keys with a random generator it has not approved, with anErroras well, so the MAC and cipher checks get the same treatment as the wrap.isKeyWrappingTransformationSupported(Keep PKCS5S2 usable on a FIPS-restricted JCE, and name the key wrapping property when the runtime has no RSA-OAEP #1058) no longer mentions MD5 and the 1024-bit key.Tests
CryptoManagerTestCase. The approved-only cases run the check in a thread of its own, set to approved-only mode withCryptoServicesRegistrar.setApprovedOnlyMode(true)(per-thread, so no other test is affected), with BC-FIPS installed first for the duration:testKeyWrappingCheckPassesUnderApprovedOnlyBcFips: RSA-OAEP passes the check.testKeyWrappingRefusedWithAnErrorIsReported:RSA/ECB/PKCS1Padding, which BC-FIPS refuses in that mode with anError, is reported withERR_CRYPTOMGR_CANNOT_GET_PREFERRED_KEY_WRAPPING_CIPHER.testMacKeyGenerationRefusedWithAnErrorIsReported,testCipherKeyGenerationRefusedWithAnErrorIsReported: anErrorfrom key generation is reported as a refusal of the MAC algorithm or the cipher transformation.testErrorWhichIsNotARefusalPropagates(review round 1): a probe provider fails the cipher, MAC and key wrapping checks with aStackOverflowErrorand with anExceptionInInitializerError; each propagates as it is, and no refusal is recorded. For the key wrapping check the probe cipher is found and fails once initialized, so the failure comes from the wrap and not fromisKeyWrappingTransformationSupported.testKeyWrappingCheckDoesNotNeedMd5replacestestKeyWrappingRefusalForAnotherCauseDoesNotNameThePropertyfrom Keep PKCS5S2 usable on a FIPS-restricted JCE, and name the key wrapping property when the runtime has no RSA-OAEP #1058, whose refusal came from MD5: without MD5 the check now passes.The crypto manager generates keys with a
SecureRandomtaken from the provider which came first when the class was loaded. In the test JVM that is SUN, and BC-FIPS in approved-only mode refuses to generate keys with it. The two key wrapping cases therefore setmac-algorithmtoHmacMD5, which approved-only BC-FIPS does not offer, so SunJCE makes the MAC key and BC-FIPS still does the wrap. The MAC and cipher cases use that refusal as theirError.Before the fix all five new cases failed: the wrap cases with the 1024-bit
FipsUnapprovedOperationError, the MAC and cipher cases with the escapedError, the MD5 case withMD5 MessageDigest not available. With the fixCryptoManagerTestCasepasses 42/42;GetSymmetricKeyExtendedOperationTestCaseandConfigureDSTestCasepass too. Three mutants were each caught by their test: the MD5 key identifier, the 1024-bit certificate, andcatch (Exception)in the key wrapping check. Five more were caught bytestErrorWhichIsNotARefusalPropagates: removing the rethrow from the cipher, MAC or key wrapping check (2 cases each), emptying the rethrow (6), and rethrowing onlyVirtualMachineError(the 3ExceptionInInitializerErrorcases).Not changed
A provider refusing the wrap itself (like PKCS#1 v1.5 above) is still reported with the generic
ERR_CRYPTOMGR_CANNOT_GET_PREFERRED_KEY_WRAPPING_CIPHER, which does not name thekey-wrapping-transformationproperty. Only a transformation the runtime does not provide at all names it (#1058).