Skip to content

Commit

Permalink
Add structure for samlp requests
Browse files Browse the repository at this point in the history
  • Loading branch information
tvdijen committed Dec 30, 2024
1 parent c8fa61a commit ba5c955
Show file tree
Hide file tree
Showing 45 changed files with 870 additions and 46 deletions.
8 changes: 4 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@

"psr/clock": "^1.0",
"psr/log": "^2.0 | ^3.0",
"simplesamlphp/assert": "^1.0",
"simplesamlphp/xml-common": "^1.14",
"simplesamlphp/xml-security": "^1.6"
"simplesamlphp/assert": "^1.6",
"simplesamlphp/xml-common": "~1.21.0",
"simplesamlphp/xml-security": "~1.11.0"
},
"require-dev": {
"beste/clock": "^3.0",
"simplesamlphp/simplesamlphp-test-framework": "^1.7"
"simplesamlphp/simplesamlphp-test-framework": "^1.8"
},
"autoload": {
"psr-4": {
Expand Down
4 changes: 2 additions & 2 deletions src/SAML11/Exception/ProtocolViolationException.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ class ProtocolViolationException extends RuntimeException
/**
* @param string $message
*/
public function __construct(string $message = null)
public function __construct(string $message = '')
{
if ($message === null) {
if ($message === '') {
if (defined('static::DEFAULT_MESSAGE')) {
$message = static::DEFAULT_MESSAGE;
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,22 @@
namespace SimpleSAML\SAML11\Exception;

/**
* This exception may be raised when a violation of the SAML11 specification is detected
* This exception may be raised when a message with a wrong version is received.
*
* @package simplesamlphp/saml11
*/
class ProtocolViolationException extends RuntimeException
class VersionMismatchException extends RuntimeException
{
/**
* @param string $message
*/
public function __construct(string $message = null)
public function __construct(string $message = '')
{
if ($message === null) {
if ($message === '') {
if (defined('static::DEFAULT_MESSAGE')) {
$message = static::DEFAULT_MESSAGE;
} else {
$message = 'A violation of the SAML11 protocol occurred.';
$message = 'A message with the wrong version was received.';
}
}

Expand Down
9 changes: 9 additions & 0 deletions src/SAML11/XML/ExtensionPointTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@
*/
trait ExtensionPointTrait
{
/**
* @inheritDoc
*/
public function getXsiType(): string
{
return $this->type;
}


/**
* Get the local name for the element's xsi:type.
*
Expand Down
133 changes: 133 additions & 0 deletions src/SAML11/XML/SignableElementTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
<?php

declare(strict_types=1);

namespace SimpleSAML\SAML11\XML;

use DOMElement;
use SimpleSAML\Assert\Assert;
use SimpleSAML\SAML11\Compat\ContainerSingleton;
use SimpleSAML\XML\DOMDocumentFactory;
use SimpleSAML\XMLSecurity\Alg\Signature\SignatureAlgorithmInterface;
use SimpleSAML\XMLSecurity\Constants as C;
use SimpleSAML\XMLSecurity\Exception\RuntimeException;
use SimpleSAML\XMLSecurity\Exception\UnsupportedAlgorithmException;
use SimpleSAML\XMLSecurity\Utils\XML;
use SimpleSAML\XMLSecurity\XML\ds\CanonicalizationMethod;
use SimpleSAML\XMLSecurity\XML\ds\KeyInfo;
use SimpleSAML\XMLSecurity\XML\ds\Signature;
use SimpleSAML\XMLSecurity\XML\ds\SignatureMethod;
use SimpleSAML\XMLSecurity\XML\ds\SignatureValue;
use SimpleSAML\XMLSecurity\XML\ds\SignedInfo;
use SimpleSAML\XMLSecurity\XML\ds\Transform;
use SimpleSAML\XMLSecurity\XML\ds\Transforms;
use SimpleSAML\XMLSecurity\XML\SignableElementTrait as BaseSignableElementTrait;

use function base64_encode;

/**
* Helper trait for processing signable elements.
*
* @package simplesamlphp/saml11
*/
trait SignableElementTrait
{
use BaseSignableElementTrait;


/**
* Sign the current element.
*
* The signature will not be applied until toXML() is called.
*
* @param \SimpleSAML\XMLSecurity\Alg\Signature\SignatureAlgorithmInterface $signer The actual signer implementation
* to use.
* @param string $canonicalizationAlg The identifier of the canonicalization algorithm to use.
* @param \SimpleSAML\XMLSecurity\XML\ds\KeyInfo|null $keyInfo A KeyInfo object to add to the signature.
*/
public function sign(
SignatureAlgorithmInterface $signer,
string $canonicalizationAlg = C::C14N_EXCLUSIVE_WITHOUT_COMMENTS,
?KeyInfo $keyInfo = null,
): void {
/**
* 5.4.2: SAML assertions and protocol messages MUST supply a value for the ID attribute
* on the root element of the assertion or protocol message being signed.
*/
Assert::notNull($this->getID(), "Signable element must have an ID set before it can be signed.");

$this->signer = $signer;
$this->keyInfo = $keyInfo;
Assert::oneOf(
$canonicalizationAlg,
[
C::C14N_INCLUSIVE_WITH_COMMENTS,
C::C14N_INCLUSIVE_WITHOUT_COMMENTS,
C::C14N_EXCLUSIVE_WITH_COMMENTS,
C::C14N_EXCLUSIVE_WITHOUT_COMMENTS,
],
'Unsupported canonicalization algorithm: %s',
UnsupportedAlgorithmException::class,
);
$this->c14nAlg = $canonicalizationAlg;
}


/**
* Do the actual signing of the document.
*
* Note that this method does not insert the signature in the returned \DOMElement. The signature will be available
* in $this->signature as a \SimpleSAML\XMLSecurity\XML\ds\Signature object, which can then be converted to XML
* calling toXML() on it, passing the \DOMElement value returned here as a parameter. The resulting \DOMElement
* can then be inserted in the position desired.
*
* E.g.:
* $xml = // our XML to sign
* $signedXML = $this->doSign($xml);
* $signedXML->appendChild($this->signature->toXML($signedXML));
*
* @param \DOMElement $xml The element to sign.
* @return \DOMElement The signed element, without the signature attached to it just yet.
*/
protected function doSign(DOMElement $xml): DOMElement
{
Assert::notNull(
$this->signer,
'Cannot call toSignedXML() without calling sign() first.',
RuntimeException::class,
);

$algorithm = $this->signer->getAlgorithmId();
$digest = $this->signer->getDigest();

$transforms = new Transforms([
/**
* 5.4.1: SAML assertions and protocols MUST use enveloped signatures when
* signing assertions and protocol messages
*/
new Transform(C::XMLDSIG_ENVELOPED),
new Transform($this->c14nAlg),
]);

$canonicalDocument = XML::processTransforms($transforms, $xml);

$signedInfo = new SignedInfo(
new CanonicalizationMethod($this->c14nAlg),
new SignatureMethod($algorithm),
[$this->getReference($digest, $transforms, $xml, $canonicalDocument)],
);

$signingData = $signedInfo->canonicalize($this->c14nAlg);
$signedData = base64_encode($this->signer->sign($signingData));

$this->signature = new Signature($signedInfo, new SignatureValue($signedData), $this->keyInfo);
return DOMDocumentFactory::fromString($canonicalDocument)->documentElement;
}


public function getBlacklistedAlgorithms(): ?array
{
$container = ContainerSingleton::getInstance();
return $container->getBlacklistedEncryptionAlgorithms();
}
}
58 changes: 58 additions & 0 deletions src/SAML11/XML/SignedElementTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

declare(strict_types=1);

namespace SimpleSAML\SAML11\XML;

use SimpleSAML\Assert\Assert;
use SimpleSAML\SAML11\Compat\ContainerSingleton;
use SimpleSAML\XMLSecurity\Exception\ReferenceValidationFailedException;
use SimpleSAML\XMLSecurity\XML\ds\Signature;
use SimpleSAML\XMLSecurity\XML\SignedElementTrait as BaseSignedElementTrait;

/**
* Helper trait for processing signed elements.
*
* @package simplesamlphp/saml11
*/
trait SignedElementTrait
{
use BaseSignedElementTrait;


/**
* Initialize a signed element from XML.
*
* @param \SimpleSAML\XMLSecurity\XML\ds\Signature $signature The ds:Signature object
*/
protected function setSignature(Signature $signature): void
{
/**
* Signatures MUST contain a single <ds:Reference> containing a same-document reference to the ID
* attribute value of the root element of the assertion or protocol message being signed. For example, if the
* ID attribute value is "foo", then the URI attribute in the <ds:Reference> element MUST be "#foo".
*/

$references = $signature->getSignedInfo()->getReferences();
Assert::count($references, 1, "A signature needs to have exactly one Reference, %d found.");

$reference = array_pop($references);
Assert::notNull($reference->getURI(), "URI attribute not found.", ReferenceValidationFailedException::class);
Assert::validURI($reference->getURI(), ReferenceValidationFailedException::class);
Assert::startsWith(
$reference->getURI(),
'#',
"Reference must contain a same-document reference to the ID-attribute of the root element.",
ReferenceValidationFailedException::class,
);

$this->signature = $signature;
}


public function getBlacklistedAlgorithms(): ?array
{
$container = ContainerSingleton::getInstance();
return $container->getBlacklistedEncryptionAlgorithms();
}
}
4 changes: 2 additions & 2 deletions src/SAML11/XML/saml/AbstractActionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ abstract class AbstractActionType extends AbstractSamlElement
*/
final public function __construct(
protected string $value,
protected string|null $Namespace = null,
protected ?string $Namespace = null,
) {
Assert::nullOrValidURI($Namespace, SchemaViolationException::class); // Covers the empty string
$this->setContent($value);
Expand Down Expand Up @@ -72,7 +72,7 @@ public static function fromXML(DOMElement $xml): static
* @param \DOMElement $parent The element we are converting to XML.
* @return \DOMElement The XML element after adding the data corresponding to this ActionType.
*/
public function toXML(DOMElement $parent = null): DOMElement
public function toXML(?DOMElement $parent = null): DOMElement
{
$e = $this->instantiateParentElement($parent);
$e->textContent = $this->getContent();
Expand Down
2 changes: 1 addition & 1 deletion src/SAML11/XML/saml/AbstractAdviceType.php
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ public static function fromXML(DOMElement $xml): static
* @param \DOMElement $parent The element we are converting to XML.
* @return \DOMElement The XML element after adding the data corresponding to this EvidenceType.
*/
public function toXML(DOMElement $parent = null): DOMElement
public function toXML(?DOMElement $parent = null): DOMElement
{
$e = $this->instantiateParentElement($parent);

Expand Down
2 changes: 1 addition & 1 deletion src/SAML11/XML/saml/AbstractAssertionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ protected function toUnsignedXML(?DOMElement $parent = null): DOMElement
* @return \DOMElement This assertion.
* @throws \Exception
*/
public function toXML(DOMElement $parent = null): DOMElement
public function toXML(?DOMElement $parent = null): DOMElement
{
if ($this->isSigned() === true && $this->signer === null) {
// We already have a signed document and no signer was set to re-sign it
Expand Down
2 changes: 1 addition & 1 deletion src/SAML11/XML/saml/AbstractAttributeStatementType.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public static function fromXML(DOMElement $xml): static
* @param \DOMElement $parent The element we are converting to XML.
* @return \DOMElement The XML element after adding the data corresponding to this AttributeStatementType.
*/
public function toXML(DOMElement $parent = null): DOMElement
public function toXML(?DOMElement $parent = null): DOMElement
{
$e = parent::toXML($parent);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public static function fromXML(DOMElement $xml): static
* @param \DOMElement $parent The element we are converting to XML.
* @return \DOMElement The XML element after adding the data corresponding to this AudienceRestrictionCondition.
*/
public function toXML(DOMElement $parent = null): DOMElement
public function toXML(?DOMElement $parent = null): DOMElement
{
$e = $this->instantiateParentElement($parent);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ public static function fromXML(DOMElement $xml): static
* @param \DOMElement $parent The element we are converting to XML.
* @return \DOMElement The XML element after adding the data corresponding to this AuthenticationStatementType.
*/
public function toXML(DOMElement $parent = null): DOMElement
public function toXML(?DOMElement $parent = null): DOMElement
{
$e = parent::toXML($parent);

Expand Down
2 changes: 1 addition & 1 deletion src/SAML11/XML/saml/AbstractAuthorityBindingType.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public static function fromXML(DOMElement $xml): static
* @param \DOMElement $parent The element we are converting to XML.
* @return \DOMElement The XML element after adding the data corresponding to this AuthorityBindingType.
*/
public function toXML(DOMElement $parent = null): DOMElement
public function toXML(?DOMElement $parent = null): DOMElement
{
$e = $this->instantiateParentElement($parent);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ public static function fromXML(DOMElement $xml): static
* @return \DOMElement The XML element after adding the data
* corresponding to this AuthorizationDecisionStatementType.
*/
public function toXML(DOMElement $parent = null): DOMElement
public function toXML(?DOMElement $parent = null): DOMElement
{
$e = parent::toXML($parent);

Expand Down
2 changes: 1 addition & 1 deletion src/SAML11/XML/saml/AbstractCondition.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ public static function fromXML(DOMElement $xml): static
* @param \DOMElement $parent The element we are converting to XML.
* @return \DOMElement The XML element after adding the data corresponding to this Condition.
*/
public function toXML(DOMElement $parent = null): DOMElement
public function toXML(?DOMElement $parent = null): DOMElement
{
$e = $this->instantiateParentElement($parent);
$e->setAttributeNS(
Expand Down
2 changes: 1 addition & 1 deletion src/SAML11/XML/saml/AbstractConditionsType.php
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ public static function fromXML(DOMElement $xml): static
* @param \DOMElement $parent The element we are converting to XML.
* @return \DOMElement The XML element after adding the data corresponding to this ConditionsType.
*/
public function toXML(DOMElement $parent = null): DOMElement
public function toXML(?DOMElement $parent = null): DOMElement
{
$e = $this->instantiateParentElement($parent);

Expand Down
2 changes: 1 addition & 1 deletion src/SAML11/XML/saml/AbstractDoNotCacheConditionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public static function fromXML(DOMElement $xml): static
* @param \DOMElement $parent The element we are converting to XML.
* @return \DOMElement The XML element after adding the data corresponding to this DoNotCacheCondition.
*/
public function toXML(DOMElement $parent = null): DOMElement
public function toXML(?DOMElement $parent = null): DOMElement
{
return $this->instantiateParentElement($parent);
}
Expand Down
2 changes: 1 addition & 1 deletion src/SAML11/XML/saml/AbstractEvidenceType.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public static function fromXML(DOMElement $xml): static
* @param \DOMElement $parent The element we are converting to XML.
* @return \DOMElement The XML element after adding the data corresponding to this EvidenceType.
*/
public function toXML(DOMElement $parent = null): DOMElement
public function toXML(?DOMElement $parent = null): DOMElement
{
$e = $this->instantiateParentElement($parent);

Expand Down
2 changes: 1 addition & 1 deletion src/SAML11/XML/saml/AbstractNameIdentifierType.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public static function fromXML(DOMElement $xml): static
* @param \DOMElement $parent The element we are converting to XML.
* @return \DOMElement The XML element after adding the data corresponding to this NameIdentifierType.
*/
public function toXML(DOMElement $parent = null): DOMElement
public function toXML(?DOMElement $parent = null): DOMElement
{
$e = $this->instantiateParentElement($parent);
$e->textContent = $this->getContent();
Expand Down
Loading

0 comments on commit ba5c955

Please sign in to comment.