2929};
3030use SimpleSAML \SAML2 \XML \samlp \Response ;
3131use SimpleSAML \XMLSecurity \Alg \Encryption \EncryptionAlgorithmFactory ;
32+ use SimpleSAML \XMLSecurity \Alg \KeyTransport \KeyTransportAlgorithmFactory ;
33+ use SimpleSAML \XMLSecurity \Alg \Signature \SignatureAlgorithmFactory ;
3234use SimpleSAML \XMLSecurity \Exception \SignatureVerificationFailedException ;
3335use SimpleSAML \XMLSecurity \XML \{
3436 EncryptableElementInterface ,
@@ -49,6 +51,9 @@ final class ServiceProvider
4951 protected ?StateProviderInterface $ stateProvider = null ;
5052 protected ?StorageProviderInterface $ storageProvider = null ;
5153 protected ?Metadata \IdentityProvider $ idpMetadata = null ;
54+ protected SignatureAlgorithmFactory $ signatureAlgorithmFactory ;
55+ protected EncryptionAlgorithmFactory $ encryptionAlgorithmFactory ;
56+ protected KeyTransportAlgorithmFactory $ keyTransportAlgorithmFactory ;
5257
5358
5459 /**
@@ -75,6 +80,9 @@ public function __construct(
7580 // Use with caution - will leave any form of constraint validation up to the implementer
7681 protected readonly bool $ bypassConstraintValidation = false ,
7782 ) {
83+ $ this ->signatureAlgorithmFactory = new SignatureAlgorithmFactory ();
84+ $ this ->encryptionAlgorithmFactory = new EncryptionAlgorithmFactory ();
85+ $ this ->keyTransportAlgorithmFactory = new KeyTransportAlgorithmFactory ();
7886 }
7987
8088
@@ -205,6 +213,10 @@ public function receiveResponse(ServerRequestInterface $request): Response
205213 );
206214 $ responseValidator ->validate ($ verifiedResponse );
207215
216+ if ($ this ->encryptedAssertions === true ) {
217+ Assert::allIsInstanceOf ($ verifiedResponse ->getAssertions (), EncryptedAssertion::class);
218+ }
219+
208220 // Decrypt and verify assertions, then rebuild the response.
209221 $ verifiedAssertions = $ this ->decryptAndVerifyAssertions ($ verifiedResponse ->getAssertions ());
210222 $ decryptedResponse = new Response (
@@ -241,6 +253,8 @@ public function receiveResponse(ServerRequestInterface $request): Response
241253 */
242254 protected function decryptAndVerifyAssertions (array $ unverifiedAssertions ): array
243255 {
256+ $ wantAssertionsSigned = $ this ->spMetadata ->getWantAssertionsSigned ();
257+
244258 /**
245259 * See paragraph 6.2 of the SAML 2.0 core specifications for the applicable processing rules
246260 *
@@ -254,6 +268,11 @@ protected function decryptAndVerifyAssertions(array $unverifiedAssertions): arra
254268 ? $ this ->decryptElement ($ assertion )
255269 : $ assertion ;
256270
271+ // Verify that the request is signed, if we require this by configuration
272+ if ($ wantAssertionsSigned === true ) {
273+ Assert::true ($ decryptedAssertion ->isSigned (), RuntimeException::class);
274+ }
275+
257276 // Verify the signature on the assertions (if any)
258277 $ verifiedAssertion = $ this ->verifyElementSignature ($ decryptedAssertion );
259278
@@ -262,7 +281,12 @@ protected function decryptAndVerifyAssertions(array $unverifiedAssertions): arra
262281
263282 if ($ nameID instanceof EncryptedID) {
264283 $ decryptedNameID = $ this ->decryptElement ($ nameID );
265- $ subject = new Subject ($ decryptedNameID , $ verifiedAssertion ->getSubjectConfirmation ());
284+ // Anything we can't decrypt, we leave up for the application to deal with
285+ try {
286+ $ subject = new Subject ($ decryptedNameID , $ verifiedAssertion ->getSubjectConfirmation ());
287+ } catch (RuntimeException ) {
288+ $ subject = $ verifiedAssertion ->getSubject ();
289+ }
266290 } else {
267291 $ subject = $ verifiedAssertion ->getSubject ();
268292 }
@@ -274,7 +298,12 @@ protected function decryptAndVerifyAssertions(array $unverifiedAssertions): arra
274298 $ attributes = $ statement ->getAttributes ();
275299 if ($ statement ->hasEncryptedAttributes ()) {
276300 foreach ($ statement ->getEncryptedAttributes () as $ encryptedAttribute ) {
277- $ attributes [] = $ this ->decryptElement ($ encryptedAttribute );
301+ // Anything we can't decrypt, we leave up for the application to deal with
302+ try {
303+ $ attributes [] = $ this ->decryptElement ($ encryptedAttribute );
304+ } catch (RuntimeException ) {
305+ $ attributes [] = $ encryptedAttribute ;
306+ }
278307 }
279308 }
280309
@@ -307,13 +336,26 @@ protected function decryptAndVerifyAssertions(array $unverifiedAssertions): arra
307336 */
308337 protected function decryptElement (EncryptedElementInterface $ element ): EncryptableElementInterface
309338 {
310- $ factory = $ this ->spMetadata ->getEncryptionAlgorithmFactory ();
339+ // TODO: When CBC-mode encryption is used, the assertion OR the Response must be signed
340+ $ factory = $ this ->encryptionAlgorithmFactory ;
311341
312- $ encryptionAlgorithm = ($ factory instanceof EncryptionAlgorithmFactory)
313- ? $ element ->getEncryptedData ()->getEncryptionMethod ()
314- : $ element ->getEncryptedKey ()->getEncryptionMethod ();
342+ // If the IDP has a pre-shared key, try decrypting with that
343+ $ preSharedKey = $ this ->idpMetadata ->getPreSharedKey ();
344+ if ($ preSharedKey !== null ) {
345+ $ encryptionAlgorithm = $ element ?->getEncryptedKey()?->getEncryptionMethod()
346+ ?? $ this ->idpMetadata ->getPreSharedKeyAlgorithm ();
347+
348+ $ decryptor = $ factory ->getAlgorithm ($ encryptionAlgorithm , $ preSharedKey );
349+ try {
350+ return $ element ->decrypt ($ decryptor );
351+ } catch (Exception $ e ) {
352+ // Continue to try decrypting with asymmetric keys.
353+ }
354+ }
315355
316- foreach ($ this ->spMetadata ->getDecriptionKeys () as $ decryptionKey ) {
356+ $ encryptionAlgorithm = $ element ->getEncryptedKey ()->getEncryptionMethod ()->getAlgorithm ();
357+ foreach ($ this ->spMetadata ->getDecryptionKeys () as $ decryptionKey ) {
358+ $ factory = $ this ->keyTransportAlgorithmFactory ;
317359 $ decryptor = $ factory ->getAlgorithm ($ encryptionAlgorithm , $ decryptionKey );
318360 try {
319361 return $ element ->decrypt ($ decryptor );
@@ -339,11 +381,10 @@ protected function decryptElement(EncryptedElementInterface $element): Encryptab
339381 */
340382 protected function verifyElementSignature (SignedElementInterface $ element ): SignableElementInterface
341383 {
342- $ factory = $ this ->spMetadata ->getSignatureAlgorithmFactory ();
343384 $ signatureAlgorithm = $ element ->getSignature ()->getSignedInfo ()->getSignatureMethod ()->getAlgorithm ();
344385
345386 foreach ($ this ->idpMetadata ->getValidatingKeys () as $ validatingKey ) {
346- $ verifier = $ factory ->getAlgorithm ($ signatureAlgorithm , $ validatingKey );
387+ $ verifier = $ this -> signatureAlgorithmFactory ->getAlgorithm ($ signatureAlgorithm , $ validatingKey );
347388
348389 try {
349390 return $ element ->verify ($ verifier );
0 commit comments