Skip to content

Commit

Permalink
[BEE-51338] Add validator and filter for checking algo used for oic c…
Browse files Browse the repository at this point in the history
…onfiguration
  • Loading branch information
pankajy-dev committed Oct 9, 2024
1 parent 769f395 commit cb59d9a
Show file tree
Hide file tree
Showing 9 changed files with 459 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ private static OidcConfiguration createFakeOidcConfiguration() {
providerMetadata.setIDTokenJWSAlgs(List.of(JWSAlgorithm.HS256));
config.setProviderMetadata(providerMetadata);
config.setPreferredJwsAlgorithm(JWSAlgorithm.HS256);
if (config.getPreferredJwsAlgorithm() != null
&& OicAlgorithmValidator.isJwsAlgorithmFipsNonCompliant(
config.getPreferredJwsAlgorithm().getName())) {
throw new IllegalArgumentException(Messages.OicConfigNonCompliantAlgo_ErrorMessage(
config.getPreferredJwsAlgorithm().getName()));
}

config.setClientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);
return config;
} catch (URISyntaxException e) {
Expand Down
160 changes: 160 additions & 0 deletions src/main/java/org/jenkinsci/plugins/oic/OicAlgorithmValidator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package org.jenkinsci.plugins.oic;

import com.nimbusds.jose.EncryptionMethod;
import com.nimbusds.jose.JWEAlgorithm;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.crypto.ECDSASigner;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jose.crypto.RSASSASigner;
import com.nimbusds.jose.crypto.impl.AESCryptoProvider;
import com.nimbusds.jose.crypto.impl.ContentCryptoProvider;
import com.nimbusds.jose.crypto.impl.ECDHCryptoProvider;
import com.nimbusds.jose.crypto.impl.PasswordBasedCryptoProvider;
import com.nimbusds.jose.crypto.impl.RSACryptoProvider;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import jenkins.security.FIPS140;

/**
* This class helps in validating algorithms for FIPS compliance and filtering the non-compliant algorithms when in
* FIPS mode.
*/
public class OicAlgorithmValidator {

private static final boolean isFIPSMode = FIPS140.useCompliantAlgorithms();

/**
* Checks if the Jws signing algorithm used for OIC configuration is FIPS compliant.
*/
public static boolean isJwsAlgorithmFipsNonCompliant(String algorithm) {
boolean matchNotFound = false;
if (isFIPSMode && algorithm != null) {
Set<JWSAlgorithm> jwsSupportedAlgorithms = new LinkedHashSet<>();
jwsSupportedAlgorithms.addAll(MACSigner.SUPPORTED_ALGORITHMS);
jwsSupportedAlgorithms.addAll(RSASSASigner.SUPPORTED_ALGORITHMS);
jwsSupportedAlgorithms.addAll(ECDSASigner.SUPPORTED_ALGORITHMS);

if (!jwsSupportedAlgorithms.isEmpty()) {
matchNotFound = jwsSupportedAlgorithms.stream()
.map(JWSAlgorithm::getName)
.noneMatch(name -> name.equals(algorithm));
}
}
return matchNotFound;
}

/**
* Checks if the Jwe encryption algorithm used for OIC configuration is FIPS compliant.
*/
public static boolean isJweAlgorithmFipsNonCompliant(String algorithm) {
boolean matchNotFound = false;
if (isFIPSMode && algorithm != null) {
Set<JWEAlgorithm> jweSupportedAlgorithms = new LinkedHashSet<>();
jweSupportedAlgorithms.addAll(AESCryptoProvider.SUPPORTED_ALGORITHMS);
jweSupportedAlgorithms.addAll(RSACryptoProvider.SUPPORTED_ALGORITHMS);
// RSA1_5 is deprecated and not a compliant algorithm.
jweSupportedAlgorithms.remove(JWEAlgorithm.RSA1_5);
jweSupportedAlgorithms.addAll(ECDHCryptoProvider.SUPPORTED_ALGORITHMS);
jweSupportedAlgorithms.addAll(PasswordBasedCryptoProvider.SUPPORTED_ALGORITHMS);

if (!jweSupportedAlgorithms.isEmpty()) {
matchNotFound = jweSupportedAlgorithms.stream()
.map(JWEAlgorithm::getName)
.noneMatch(name -> name.equals(algorithm));
}
}
return matchNotFound;
}

/**
* Filter FIPS non-compliant Jwe encryption algorithm used for OIC configuration.
*/
public static void filterFipsNonCompliantJweAlgorithm(List<JWEAlgorithm> algorithm) {
boolean matchNotFound = false;
if (isFIPSMode && algorithm != null && !algorithm.isEmpty()) {
List<JWEAlgorithm> itemsToBeRemoved = new ArrayList<>();
for (JWEAlgorithm jweAlgorithm : algorithm) {
matchNotFound = isJweAlgorithmFipsNonCompliant(jweAlgorithm.getName());
if (matchNotFound) {
itemsToBeRemoved.add(jweAlgorithm);
}
}
if (!itemsToBeRemoved.isEmpty()) {
algorithm.removeAll(itemsToBeRemoved);
}
}
}

/**
* validate FIPS non-compliant Jwe encryption algorithm used for OIC configuration.
*/
public static boolean isJwsAlgoFipsNonCompliant(List<JWSAlgorithm> algorithm) {
boolean matchNotFound = false;
if (isFIPSMode && algorithm != null && !algorithm.isEmpty()) {
for (JWSAlgorithm jwsAlgorithm : algorithm) {
matchNotFound = isJwsAlgorithmFipsNonCompliant(jwsAlgorithm.getName());
if (matchNotFound) {
return true;
}
}
}
return false;
}

/**
* Filter FIPS non-compliant Jws encryption algorithm used for OIC configuration.
*/
public static void filterFipsNonCompliantJwsAlgorithm(List<JWSAlgorithm> algorithm) {
boolean matchNotFound = false;
if (isFIPSMode && algorithm != null && !algorithm.isEmpty()) {
List<JWSAlgorithm> itemsToBeRemoved = new ArrayList<>();
for (JWSAlgorithm jwsAlgorithm : algorithm) {
matchNotFound = isJwsAlgorithmFipsNonCompliant(jwsAlgorithm.getName());
if (matchNotFound) {
itemsToBeRemoved.add(jwsAlgorithm);
}
}
if (!itemsToBeRemoved.isEmpty()) {
algorithm.removeAll(itemsToBeRemoved);
}
}
}

/**
* Checks if the encryption method used for OIC configuration is FIPS compliant.
*/
public static boolean isEncryptionMethodFipsNonCompliant(String encryptionMethod) {
boolean matchNotFound = false;
if (isFIPSMode && encryptionMethod != null) {
Set<EncryptionMethod> supportedEncryptionMethod =
new LinkedHashSet<>(ContentCryptoProvider.SUPPORTED_ENCRYPTION_METHODS);
if (!supportedEncryptionMethod.isEmpty()) {
matchNotFound = supportedEncryptionMethod.stream()
.map(EncryptionMethod::getName)
.noneMatch(name -> name.equals(encryptionMethod));
}
}
return matchNotFound;
}

/**
* Filter FIPS non-compliant encryption algorithm used for OIC configuration.
*/
public static void filterFipsNonCompliantEncryptionMethod(List<EncryptionMethod> algorithm) {
boolean matchNotFound = false;
if (isFIPSMode && algorithm != null && !algorithm.isEmpty()) {
List<EncryptionMethod> itemsToBeRemoved = new ArrayList<>();
for (EncryptionMethod encryptionMethod : algorithm) {
matchNotFound = isEncryptionMethodFipsNonCompliant(encryptionMethod.getName());
if (matchNotFound) {
itemsToBeRemoved.add(encryptionMethod);
}
}
if (!itemsToBeRemoved.isEmpty()) {
algorithm.removeAll(itemsToBeRemoved);
}
}
}
}
7 changes: 7 additions & 0 deletions src/main/java/org/jenkinsci/plugins/oic/OicSecurityRealm.java
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,13 @@ private OidcConfiguration buildOidcConfiguration() {
// conf.setPreferredJwsAlgorithm(JWSAlgorithm.HS256);
// set many more as needed...

// TODO if we set PreferredJwsAlgo then check for the validation
// if(conf.getPreferredJwsAlgorithm() != null && OicAlgorithmValidator.isAlgorithmNotFipsCompliant(
// conf.getPreferredJwsAlgorithm().getName())){
// throw new
// IllegalArgumentException(Messages.OicConfigNonCompliantAlgo_ErrorMessage(conf.getPreferredJwsAlgorithm().getName()));
// }

OIDCProviderMetadata oidcProviderMetadata = serverConfiguration.toProviderMetadata();
if (this.isDisableTokenVerification()) {
conf.setAllowUnsignedIdTokens(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,11 @@ public OIDCProviderMetadata toProviderMetadata() {
ArrayList<JWSAlgorithm> allAlgorthms = new ArrayList<>();
allAlgorthms.addAll(JWSAlgorithm.Family.HMAC_SHA);
allAlgorthms.addAll(JWSAlgorithm.Family.SIGNATURE);

if (!allAlgorthms.isEmpty() && OicAlgorithmValidator.isJwsAlgoFipsNonCompliant(allAlgorthms)) {
throw new IllegalArgumentException(
Messages.OicConfigNonCompliantAlgo_ErrorMessage("Non-compliant algo found."));
}
providerMetadata.setIDTokenJWSAlgs(allAlgorthms);
return providerMetadata;
} catch (URISyntaxException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ public OIDCProviderMetadata toProviderMetadata() {
OIDCProviderMetadata _oidcProviderMetadata =
OIDCProviderMetadata.parse(rr.retrieveResource(new URL(wellKnownOpenIDConfigurationUrl))
.getContent());
filterNonCompliantAlgorithms(_oidcProviderMetadata);
String _scopesOverride = getScopesOverride();
if (_scopesOverride != null) {
// split the scopes by space
Expand Down Expand Up @@ -158,6 +159,35 @@ public OIDCProviderMetadata toProviderMetadata() {
throw new IllegalStateException("Well known configuration could not be loaded, login can not preceed.");
}

/**
* Filter {@link OIDCProviderMetadata} for all the JWS/JWE algorithms and Encryption methods
*/
protected static void filterNonCompliantAlgorithms(OIDCProviderMetadata oidcProviderMetadata) {

// Filter Jws Algorithms
OicAlgorithmValidator.filterFipsNonCompliantJwsAlgorithm(oidcProviderMetadata.getIDTokenJWSAlgs());
OicAlgorithmValidator.filterFipsNonCompliantJwsAlgorithm(oidcProviderMetadata.getTokenEndpointJWSAlgs());
OicAlgorithmValidator.filterFipsNonCompliantJwsAlgorithm(
oidcProviderMetadata.getIntrospectionEndpointJWSAlgs());
OicAlgorithmValidator.filterFipsNonCompliantJwsAlgorithm(oidcProviderMetadata.getRevocationEndpointJWSAlgs());
OicAlgorithmValidator.filterFipsNonCompliantJwsAlgorithm(oidcProviderMetadata.getRequestObjectJWSAlgs());
OicAlgorithmValidator.filterFipsNonCompliantJwsAlgorithm(oidcProviderMetadata.getDPoPJWSAlgs());
OicAlgorithmValidator.filterFipsNonCompliantJwsAlgorithm(oidcProviderMetadata.getAuthorizationJWSAlgs());
OicAlgorithmValidator.filterFipsNonCompliantJwsAlgorithm(
oidcProviderMetadata.getBackChannelAuthenticationRequestJWSAlgs());
OicAlgorithmValidator.filterFipsNonCompliantJwsAlgorithm(
oidcProviderMetadata.getBackChannelAuthenticationRequestJWSAlgs());

// Filter Jwe Algorithms
OicAlgorithmValidator.filterFipsNonCompliantJweAlgorithm(oidcProviderMetadata.getIDTokenJWEAlgs());
OicAlgorithmValidator.filterFipsNonCompliantJweAlgorithm(oidcProviderMetadata.getRequestObjectJWEAlgs());
OicAlgorithmValidator.filterFipsNonCompliantJweAlgorithm(oidcProviderMetadata.getAuthorizationJWEAlgs());

// Filter Encryption methods
OicAlgorithmValidator.filterFipsNonCompliantEncryptionMethod(oidcProviderMetadata.getRequestObjectJWEEncs());
OicAlgorithmValidator.filterFipsNonCompliantEncryptionMethod(oidcProviderMetadata.getAuthorizationJWEEncs());
}

/**
* Parse headers to determine expiration date.
* Sets the expiry time to 1 hour from the current time if the header is not available.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,5 @@ OicSecurityRealm.TokenRequestFailure = Token request failed: {0}"
OicSecurityRealm.TokenRefreshFailure = Unable to refresh access token
OicServerWellKnownConfiguration.DisplayName = Discovery via well-known endpoint
OicServerManualConfiguration.DisplayName = Manual entry
OicConfigNonCompliantAlgo.ErrorMessage = Algorithm ''{0}'' used for OIC configuration is not FIPS compliant.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.jenkinsci.plugins.oic;

import jenkins.security.FIPS140;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mockStatic;

class OicAlgorithmValidatorTest {

private MockedStatic<FIPS140> fips140Mock;

@BeforeEach
void setUp() {
fips140Mock = mockStatic(FIPS140.class);
}

@Test
void isJwsAlgorithmFipsCompliant() {
fips140Mock.when(FIPS140::useCompliantAlgorithms).thenReturn(true);
assertTrue(OicAlgorithmValidator.isJwsAlgorithmFipsNonCompliant(""));
assertTrue(OicAlgorithmValidator.isJwsAlgorithmFipsNonCompliant(" "));
assertTrue(OicAlgorithmValidator.isJwsAlgorithmFipsNonCompliant("invalid-algo"));

String[] validAlgoArray = {
"HS256", "HS384", "HS512", "RS256", "RS384", "RS512", "ES256", "ES256K", "ES384", "ES512", "PS256", "PS384",
"PS512"
};
for (String algo : validAlgoArray) {
assertFalse(OicAlgorithmValidator.isJwsAlgorithmFipsNonCompliant(algo));
}
assertTrue(OicAlgorithmValidator.isJwsAlgorithmFipsNonCompliant("EdDSA"));
assertTrue(OicAlgorithmValidator.isJwsAlgorithmFipsNonCompliant("Ed25519"));
assertTrue(OicAlgorithmValidator.isJwsAlgorithmFipsNonCompliant("Ed448"));
}
}
Loading

0 comments on commit cb59d9a

Please sign in to comment.