Issues (3)

src/SAML11/XML/SignableElementTrait.php (2 issues)

1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\SAML11\XML;
6
7
use DOMElement;
8
use SimpleSAML\Assert\Assert;
9
use SimpleSAML\SAML11\Compat\ContainerSingleton;
10
use SimpleSAML\XML\DOMDocumentFactory;
11
use SimpleSAML\XMLSchema\Type\{AnyURIValue, Base64BinaryValue};
12
use SimpleSAML\XMLSecurity\Alg\Signature\SignatureAlgorithmInterface;
13
use SimpleSAML\XMLSecurity\Constants as C;
14
use SimpleSAML\XMLSecurity\Exception\{RuntimeException, UnsupportedAlgorithmException};
15
use SimpleSAML\XMLSecurity\Utils\XML;
16
use SimpleSAML\XMLSecurity\XML\ds\{
17
    CanonicalizationMethod,
18
    KeyInfo,
19
    Signature,
20
    SignatureMethod,
21
    SignatureValue,
22
    SignedInfo,
23
    Transform,
24
    Transforms,
25
};
26
use SimpleSAML\XMLSecurity\XML\SignableElementTrait as BaseSignableElementTrait;
27
28
use function base64_encode;
29
30
/**
31
 * Helper trait for processing signable elements.
32
 *
33
 * @package simplesamlphp/saml11
34
 */
35
trait SignableElementTrait
36
{
37
    use BaseSignableElementTrait;
38
39
40
    /**
41
     * Sign the current element.
42
     *
43
     * The signature will not be applied until toXML() is called.
44
     *
45
     * @param \SimpleSAML\XMLSecurity\Alg\Signature\SignatureAlgorithmInterface $signer The actual signer implementation
46
     * to use.
47
     * @param string $canonicalizationAlg The identifier of the canonicalization algorithm to use.
48
     * @param \SimpleSAML\XMLSecurity\XML\ds\KeyInfo|null $keyInfo A KeyInfo object to add to the signature.
49
     */
50
    public function sign(
51
        SignatureAlgorithmInterface $signer,
52
        string $canonicalizationAlg = C::C14N_EXCLUSIVE_WITHOUT_COMMENTS,
53
        ?KeyInfo $keyInfo = null,
54
    ): void {
55
        /**
56
         * 5.4.2: SAML assertions and protocol messages MUST supply a value for the ID attribute
57
         * on the root element of the assertion or protocol message being signed.
58
         */
59
        Assert::notNull($this->getID(), "Signable element must have an ID set before it can be signed.");
60
61
        $this->signer = $signer;
62
        $this->keyInfo = $keyInfo;
63
        Assert::oneOf(
64
            $canonicalizationAlg,
65
            C::$CANONICALIZATION_ALGORITHMS,
66
            'Unsupported canonicalization algorithm: %s',
67
            UnsupportedAlgorithmException::class,
68
        );
69
        $this->c14nAlg = $canonicalizationAlg;
70
    }
71
72
73
    /**
74
     * Do the actual signing of the document.
75
     *
76
     * Note that this method does not insert the signature in the returned \DOMElement. The signature will be available
77
     * in $this->signature as a \SimpleSAML\XMLSecurity\XML\ds\Signature object, which can then be converted to XML
78
     * calling toXML() on it, passing the \DOMElement value returned here as a parameter. The resulting \DOMElement
79
     * can then be inserted in the position desired.
80
     *
81
     * E.g.:
82
     *     $xml = // our XML to sign
83
     *     $signedXML = $this->doSign($xml);
84
     *     $signedXML->appendChild($this->signature->toXML($signedXML));
85
     *
86
     * @param \DOMElement $xml The element to sign.
87
     * @return \DOMElement The signed element, without the signature attached to it just yet.
88
     */
89
    protected function doSign(DOMElement $xml): DOMElement
90
    {
91
        Assert::notNull(
92
            $this->signer,
93
            'Cannot call toSignedXML() without calling sign() first.',
94
            RuntimeException::class,
95
        );
96
97
        $algorithm = $this->signer->getAlgorithmId();
0 ignored issues
show
The method getAlgorithmId() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

97
        /** @scrutinizer ignore-call */ 
98
        $algorithm = $this->signer->getAlgorithmId();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
98
        $digest = $this->signer->getDigest();
99
100
        $transforms = new Transforms([
101
            /**
102
             * 5.4.1: SAML assertions and protocols MUST use enveloped signatures when
103
             * signing assertions and protocol messages
104
             */
105
            new Transform(
106
                AnyURIValue::fromString(C::XMLDSIG_ENVELOPED),
107
            ),
108
            new Transform(
109
                AnyURIValue::fromString($this->c14nAlg),
110
            ),
111
        ]);
112
113
        $canonicalDocument = XML::processTransforms($transforms, $xml);
114
115
        $signedInfo = new SignedInfo(
116
            new CanonicalizationMethod(
117
                AnyURIValue::fromString($this->c14nAlg),
118
            ),
119
            new SignatureMethod(
120
                AnyURIValue::fromString($algorithm),
121
            ),
122
            [$this->getReference($digest, $transforms, $xml, $canonicalDocument)],
123
        );
124
125
        $signingData = $signedInfo->canonicalize($this->c14nAlg);
126
        $signedData = base64_encode($this->signer->sign($signingData));
0 ignored issues
show
It seems like $this->signer->sign($signingData) can also be of type false; however, parameter $string of base64_encode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

126
        $signedData = base64_encode(/** @scrutinizer ignore-type */ $this->signer->sign($signingData));
Loading history...
127
128
        $this->signature = new Signature(
129
            $signedInfo,
130
            new SignatureValue(
131
                Base64BinaryValue::fromString($signedData),
132
            ),
133
            $this->keyInfo,
134
        );
135
136
        return DOMDocumentFactory::fromString($canonicalDocument)->documentElement;
137
    }
138
139
140
    /**
141
     * @return array|null
142
     */
143
    public function getBlacklistedAlgorithms(): ?array
144
    {
145
        $container = ContainerSingleton::getInstance();
146
        return $container->getBlacklistedEncryptionAlgorithms();
147
    }
148
}
149