[java-identity-provider] branch maint-4 updated: IDP-2054 - Add per-SP salt for computedId connector

Scott Cantor cantor.2 at osu.edu
Wed Dec 14 14:38:20 UTC 2022


This is an automated email from the git hooks/post-receive script.

scantor pushed a commit to branch maint-4
in repository java-identity-provider.

View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=d1efa94ef3ced285d527428d9fe5097b46a6e81e

The following commit(s) were added to refs/heads/maint-4 by this push:
     new d1efa94ef IDP-2054 - Add per-SP salt for computedId connector
d1efa94ef is described below

commit d1efa94ef3ced285d527428d9fe5097b46a6e81e
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed Dec 14 09:38:17 2022 -0500

    IDP-2054 - Add per-SP salt for computedId connector
    
    https://shibboleth.atlassian.net/browse/IDP-2054
---
 .../attribute/impl/ComputedPairwiseIdStore.java    | 74 +++++++++++++++-------
 .../impl/ComputedPairwiseIdStoreTest.java          | 17 +++++
 .../dc/impl/ComputedIdDataConnectorParser.java     |  4 ++
 .../idp/attribute/resolver/spring/customBean.xml   |  8 ++-
 .../resolver/spring/dc/resolver/computed.xml       |  1 +
 .../resolver/spring/dc/resolver/stored.xml         |  1 +
 .../net/shibboleth/idp/conf/saml-nameid-system.xml |  1 +
 .../schema/shibboleth-attribute-resolver.xsd       | 20 +++++-
 8 files changed, 100 insertions(+), 26 deletions(-)

diff --git a/idp-attribute-impl/src/main/java/net/shibboleth/idp/attribute/impl/ComputedPairwiseIdStore.java b/idp-attribute-impl/src/main/java/net/shibboleth/idp/attribute/impl/ComputedPairwiseIdStore.java
index 7368c0451..8a4bb261b 100644
--- a/idp-attribute-impl/src/main/java/net/shibboleth/idp/attribute/impl/ComputedPairwiseIdStore.java
+++ b/idp-attribute-impl/src/main/java/net/shibboleth/idp/attribute/impl/ComputedPairwiseIdStore.java
@@ -23,6 +23,7 @@ import java.security.NoSuchAlgorithmException;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.function.BiFunction;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -41,12 +42,20 @@ import net.shibboleth.utilities.java.support.component.ComponentSupport;
 import net.shibboleth.utilities.java.support.logic.Constraint;
 import net.shibboleth.utilities.java.support.primitive.StringSupport;
 
+import org.opensaml.profile.context.ProfileRequestContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
  * A {@link PairwiseIdStore} that generates a pairwise ID by computing the hash of
- * a given attribute value, the entity ID of the recipient, and a provided salt.
+ * a given attribute value, the entity ID of the recipient, and a salt.
+ * 
+ * <p>The salt may be global, or produced by a lookup function, and an exception map
+ * may be injected to override those values. The precedence is [map, function, global],
+ * and either a global value or function must be supplied.<.p>
+ * 
+ * <p>In this version of the software, the first argument to the salt strategy function
+ * will always be null. Future versions will change this.</p>
  * 
  * <p>The original implementation and values in common use relied on base64 encoding of the result,
  * but due to discovery of the lack of appropriate case handling of identifiers by applications, the
@@ -71,7 +80,7 @@ public class ComputedPairwiseIdStore extends AbstractInitializableComponent impl
         BASE32,
     };
 
-    /** Salt used when computing the ID. */
+    /** Default salt used when computing the ID. */
     @NonnullAfterInit private byte[] salt;
 
     /** JCE digest algorithm name to use. */
@@ -83,6 +92,9 @@ public class ComputedPairwiseIdStore extends AbstractInitializableComponent impl
     /** Override map to block or re-issue identifiers. */
     @Nonnull private Map<String,Map<String,String>> exceptionMap;
     
+    /** Optional source of salt for request. */
+    @Nullable BiFunction<ProfileRequestContext,PairwiseId,String> saltLookupStrategy;
+    
     /** Constructor. */
     public ComputedPairwiseIdStore() {
         algorithm = "SHA";
@@ -222,6 +234,19 @@ public class ComputedPairwiseIdStore extends AbstractInitializableComponent impl
             }
         }
     }
+    
+    /**
+     * Sets an optional function to use to obtain the salt for the request.
+     * 
+     * @param strategy lookup strategy
+     * 
+     * @since 4.3.0
+     */
+    public void setSaltLookupStrategy(@Nullable final BiFunction<ProfileRequestContext,PairwiseId,String> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        saltLookupStrategy = strategy;
+    }
 
     /** {@inheritDoc} */
     @Override
@@ -229,10 +254,10 @@ public class ComputedPairwiseIdStore extends AbstractInitializableComponent impl
         super.doInitialize();
         
         if (null == getSalt()) {
-            throw new ComponentInitializationException("Salt cannot be null");
-        }
-
-        if (getSalt().length < 16) {
+            if (saltLookupStrategy == null) {
+                throw new ComponentInitializationException("Global salt and salt lookup strategy cannot both be null");
+            }
+        } else if (getSalt().length < 16) {
             throw new ComponentInitializationException("Salt must be at least 16 bytes in size");
         }
     }
@@ -246,10 +271,10 @@ public class ComputedPairwiseIdStore extends AbstractInitializableComponent impl
         Constraint.isNotEmpty(pid.getPrincipalName(), "Principal name cannot be null or empty");
         Constraint.isNotEmpty(pid.getSourceSystemId(), "Source system ID cannot be null or empty");
     
-        final byte[] effectiveSalt = getEffectiveSalt(pid.getPrincipalName(), pid.getRecipientEntityID());
+        final byte[] effectiveSalt = getEffectiveSalt(pid);
         if (effectiveSalt == null) {
-            log.warn("Pairwise ID generation blocked for relying party ({})", pid.getRecipientEntityID());
-            throw new IOException("Pairwise ID generation blocked by exception rule");
+            log.warn("Pairwise ID generation blocked for relying party ({}), no salt available", pid.getRecipientEntityID());
+            throw new IOException("Pairwise ID generation blocked due to absence of salt");
         }
         
         try {
@@ -280,43 +305,48 @@ public class ComputedPairwiseIdStore extends AbstractInitializableComponent impl
     /**
      * Get the effective salt to apply for a particular principal/RP pair, or null to refuse to generate one.
      * 
-     * @param principalName name of subject
-     * @param relyingPartyId name of relying party scope
+     * @param pid pairwise ID input
      * 
      * @return salt to use
      */
-    @Nullable private byte[] getEffectiveSalt(@Nonnull @NotEmpty final String principalName,
-            @Nonnull @NotEmpty final String relyingPartyId) {
+    @Nullable private byte[] getEffectiveSalt(@Nonnull final PairwiseId pid) {
         
-        Map<String,String> override = exceptionMap.get(principalName);
+        Map<String,String> override = exceptionMap.get(pid.getPrincipalName());
         if (override == null) {
             override = exceptionMap.get(WILDCARD_OVERRIDE);
         }
         
         if (override != null) {
-            if (override.containsKey(relyingPartyId)) {
-                final String s = override.get(relyingPartyId);
+            if (override.containsKey(pid.getRecipientEntityID())) {
+                final String s = override.get(pid.getRecipientEntityID());
                 if (s != null) {
-                    log.debug("Overriding salt for principal '{}' and relying party '{}'", principalName,
-                            relyingPartyId);
+                    log.debug("Overriding salt for principal '{}' and relying party '{}'", pid.getPrincipalName(),
+                            pid.getRecipientEntityID());
                     return s.getBytes();
                 }
                 log.debug("Blocked generation of ID for principal '{}' for relying party '{}'",
-                        principalName, relyingPartyId);
+                        pid.getPrincipalName(), pid.getRecipientEntityID());
                 return null;
             } else if (override.containsKey(WILDCARD_OVERRIDE)) {
                 final String s = override.get(WILDCARD_OVERRIDE);
                 if (s != null) {
-                    log.debug("Overriding salt for principal '{}' and relying party '{}'", principalName,
-                            relyingPartyId);
+                    log.debug("Overriding salt for principal '{}' and relying party '{}'", pid.getPrincipalName(),
+                            pid.getRecipientEntityID());
                     return s.getBytes();
                 }
                 log.debug("Blocked generation of ID for principal '{}' for relying party '{}'",
-                        principalName, relyingPartyId);
+                        pid.getPrincipalName(), pid.getRecipientEntityID());
                 return null;
             }
         }
         
+        if (saltLookupStrategy != null) {
+            final String derivedSalt = saltLookupStrategy.apply(null, pid);
+            if (derivedSalt != null) {
+                return derivedSalt.getBytes();
+            }
+        }
+        
         return salt;
     }
     
diff --git a/idp-attribute-impl/src/test/java/net/shibboleth/idp/attribute/impl/ComputedPairwiseIdStoreTest.java b/idp-attribute-impl/src/test/java/net/shibboleth/idp/attribute/impl/ComputedPairwiseIdStoreTest.java
index 6f5a9f0ab..50006eed0 100644
--- a/idp-attribute-impl/src/test/java/net/shibboleth/idp/attribute/impl/ComputedPairwiseIdStoreTest.java
+++ b/idp-attribute-impl/src/test/java/net/shibboleth/idp/attribute/impl/ComputedPairwiseIdStoreTest.java
@@ -39,6 +39,8 @@ public class ComputedPairwiseIdStoreTest {
     /** Value calculated using V2 version. DO NOT CHANGE WITHOUT TESTING AGAINST 2.0 */
     private static final String RESULT = "Vl6z6K70iLc4AuBoNeb59Dj1rGw=";
 
+    private static final String RESULT_BY_FUNCTION = "ZPNsH0Q/K8s48wLwFdviHuFPuWY=";
+    
     private static final String RESULT2 = "kLyH1uEvYigEvg1ZLh/QXeW1VAs=";
 
     private static final String B32RESULT = "KZPLH2FO6SELOOAC4BUDLZXZ6Q4PLLDM";
@@ -47,6 +49,8 @@ public class ComputedPairwiseIdStoreTest {
 
     private static final String salt2 = "thisisaspecialsalt";
     
+    private static final String saltByFunction = "thisisasaltfromfunction";
+    
     private static final String INVALID_BASE64_SALT="AB==";
         
     public static final String COMMON_ATTRIBUTE_VALUE_STRING = "at1-Data";
@@ -125,6 +129,12 @@ public class ComputedPairwiseIdStoreTest {
     public void testComputedId() throws Exception {
         final ComputedPairwiseIdStore store = new ComputedPairwiseIdStore();
         store.setSalt(salt);
+        store.setSaltLookupStrategy((prc,pid) -> {
+            if ("fooByFunction".equals(pid.getPrincipalName())) {
+                return saltByFunction;
+            }
+            return null;
+        });
         store.initialize();
 
         PairwiseId pid = new PairwiseId();
@@ -136,6 +146,13 @@ public class ComputedPairwiseIdStoreTest {
 
         Assert.assertNotNull(pid);
         Assert.assertEquals(pid.getPairwiseId(), RESULT);
+        
+        // Trigger function override.
+        pid.setPrincipalName("fooByFunction");
+        pid = store.getBySourceValue(pid, true);
+
+        Assert.assertNotNull(pid);
+        Assert.assertEquals(pid.getPairwiseId(), RESULT_BY_FUNCTION);
     }
     
     @Test
diff --git a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/dc/impl/ComputedIdDataConnectorParser.java b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/dc/impl/ComputedIdDataConnectorParser.java
index 1b3c78106..f45cc256a 100644
--- a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/dc/impl/ComputedIdDataConnectorParser.java
+++ b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/dc/impl/ComputedIdDataConnectorParser.java
@@ -76,6 +76,10 @@ public class ComputedIdDataConnectorParser extends PairwiseIdDataConnectorParser
         if (config.hasAttributeNS(null, "exceptionMapRef")) {
             builder.addPropertyReference("exceptionMap", config.getAttributeNS(null, "exceptionMapRef"));
         }
+        
+        if (config.hasAttributeNS(null, "saltLookupStrategyRef")) {
+            builder.addPropertyReference("saltLookupStrategy", config.getAttributeNS(null, "saltLookupStrategyRef"));
+        }
 
         final String salt;
         final String encodedSalt;
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/customBean.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/customBean.xml
index 8cd7aca6c..ed7728213 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/customBean.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/customBean.xml
@@ -62,7 +62,11 @@
             </property>
         </bean>
     </util:list>
+    
+    <bean id="SaltLookupStrategy" class="net.shibboleth.utilities.java.support.logic.BiFunctionSupport" factory-method="constant">
+        <constructor-arg>
+            <null/>
+        </constructor-arg>
+    </bean>
 
 </beans>
-
-       
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/resolver/computed.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/resolver/computed.xml
index 01945a0f1..a8928678f 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/resolver/computed.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/resolver/computed.xml
@@ -4,6 +4,7 @@
 	
 	generatedAttributeID="jenny"
 	salt="abcdefghijklmnopqrst "
+    saltLookupStrategyRef="SaltLookupStrategy"
 	algorithm="SHA256"
 	encoding="BASE32"
 	xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd">
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/resolver/stored.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/resolver/stored.xml
index 77214681c..5081bcb44 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/resolver/stored.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/resolver/stored.xml
@@ -8,6 +8,7 @@
     retryableErrors="25000 25001"
 	generatedAttributeID="jenny"
 	salt="abcdefghijklmnopqrst"
+    saltLookupStrategyRef="SaltLookupStrategy"
 	
 	xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd">
 
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/conf/saml-nameid-system.xml b/idp-conf-impl/src/main/resources/net/shibboleth/idp/conf/saml-nameid-system.xml
index cc1336119..2ba7ab2f1 100644
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/conf/saml-nameid-system.xml
+++ b/idp-conf-impl/src/main/resources/net/shibboleth/idp/conf/saml-nameid-system.xml
@@ -62,6 +62,7 @@
         p:salt="%{idp.persistentId.salt:}"
         p:encodedSalt="%{idp.persistentId.encodedSalt:}"
         p:exceptionMap="#{getObject('%{idp.persistentId.exceptionMap:shibboleth.ComputedIdExceptionMap}'.trim())}"
+        p:saltLookupStrategy="#{getObject('shibboleth.ComputedIdSaltLookupStrategy')}"
         p:algorithm="%{idp.persistentId.algorithm:SHA}"
         p:encoding="#{ T(net.shibboleth.idp.attribute.impl.ComputedPairwiseIdStore.Encoding).%{idp.persistentId.encoding:BASE64} }" />
 
diff --git a/idp-schema/src/main/resources/schema/shibboleth-attribute-resolver.xsd b/idp-schema/src/main/resources/schema/shibboleth-attribute-resolver.xsd
index d83497c81..fbddae33e 100644
--- a/idp-schema/src/main/resources/schema/shibboleth-attribute-resolver.xsd
+++ b/idp-schema/src/main/resources/schema/shibboleth-attribute-resolver.xsd
@@ -821,7 +821,7 @@
                         </documentation>
                     </annotation>
                 </attribute>
-                <attribute name="exceptionMapRef" type="string">
+                <attribute name="exceptionMapRef" type="resolver:string">
                     <annotation>
                         <documentation>
                             Spring bean reference to a map of overrides that alter salt or suppress generation
@@ -829,6 +829,14 @@
                         </documentation>
                     </annotation>
                 </attribute>
+                <attribute name="saltLookupStrategyRef" type="resolver:string">
+                    <annotation>
+                        <documentation>
+                            Spring bean reference to a BiFunction<ProfileRequestContext,PairwiseId> 
+                            to obtain the salt.
+                        </documentation>
+                    </annotation>
+                </attribute>
             </extension>
         </complexContent>
     </complexType>
@@ -1749,7 +1757,7 @@
                         </documentation>
                     </annotation>
                 </attribute>
-                <attribute name="exceptionMapRef" type="string">
+                <attribute name="exceptionMapRef" type="resolver:string">
                     <annotation>
                         <documentation>
                             Spring bean reference to a map of overrides that alter salt or suppress generation
@@ -1757,6 +1765,14 @@
                         </documentation>
                     </annotation>
                 </attribute>
+                <attribute name="saltLookupStrategyRef" type="resolver:string">
+                    <annotation>
+                        <documentation>
+                            Spring bean reference to a BiFunction<ProfileRequestContext,PairwiseId> 
+                            to obtain the salt.
+                        </documentation>
+                    </annotation>
+                </attribute>
             </extension>
         </complexContent>
     </complexType>

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list