Passed
Pull Request — master (#359)
by Tim
02:55
created

SignableElementTrait::doSign()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 33
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

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

99
        /** @scrutinizer ignore-call */ 
100
        $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...
100
        $digest = $this->signer->getDigest();
101
102
        $transforms = new Transforms([
103
            /**
104
             * 5.4.1: SAML assertions and protocols MUST use enveloped signatures when
105
             * signing assertions and protocol messages
106
             */
107
            new Transform(C::XMLDSIG_ENVELOPED),
108
            new Transform($this->c14nAlg),
109
        ]);
110
111
        $canonicalDocument = XML::processTransforms($transforms, $xml);
112
113
        $signedInfo = new SignedInfo(
114
            new CanonicalizationMethod($this->c14nAlg),
115
            new SignatureMethod($algorithm),
116
            [$this->getReference($digest, $transforms, $xml, $canonicalDocument)],
117
        );
118
119
        $signingData = $signedInfo->canonicalize($this->c14nAlg);
120
        $signedData = base64_encode($this->signer->sign($signingData));
0 ignored issues
show
Bug introduced by
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

120
        $signedData = base64_encode(/** @scrutinizer ignore-type */ $this->signer->sign($signingData));
Loading history...
121
122
        $this->signature = new Signature($signedInfo, new SignatureValue($signedData), $this->keyInfo);
123
        return DOMDocumentFactory::fromString($canonicalDocument)->documentElement;
124
    }
125
}
126