Passed
Push — master ( 567a59...2c10ef )
by Tim
01:40
created

SignedElementTestTrait::testSignatures()   C

Complexity

Conditions 10
Paths 16

Size

Total Lines 128
Code Lines 78

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 78
nc 16
nop 0
dl 0
loc 128
rs 6.6133
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\XMLSecurity\TestUtils;
6
7
use DOMDocument;
8
use Exception;
9
use SimpleSAML\XMLSecurity\Alg\Signature\SignatureAlgorithmFactory;
10
use SimpleSAML\XMLSecurity\Constants as C;
11
use SimpleSAML\XMLSecurity\Exception\InvalidArgumentException;
12
use SimpleSAML\XMLSecurity\Exception\NoSignatureFoundException;
13
use SimpleSAML\XMLSecurity\Exception\SignatureVerificationFailedException;
14
use SimpleSAML\XMLSecurity\Exception\UnsupportedAlgorithmException;
15
use SimpleSAML\XMLSecurity\Key\PrivateKey;
16
use SimpleSAML\XMLSecurity\Key\X509Certificate as X509;
17
use SimpleSAML\XMLSecurity\XML\ds\KeyInfo;
18
use SimpleSAML\XMLSecurity\XML\ds\X509Certificate;
19
use SimpleSAML\XMLSecurity\XML\ds\X509Data;
20
use SimpleSAML\XMLSecurity\TestUtils\PEMCertificatesMock;
21
use SimpleSAML\XMLSecurity\Utils\Certificate as CertificateUtils;
22
23
use function array_keys;
24
use function boolval;
25
use function class_exists;
26
use function hexdec;
27
use function sprintf;
28
29
/**
30
 * A trait providing basic tests for signed elements.
31
 *
32
 * Only to be used by classes extending \PHPUnit\Framework\TestCase. Make sure to assign the class name of the class
33
 * you are testing to the $testedClass property.
34
 *
35
 * @package simplesamlphp/xml-security
36
 */
37
trait SignedElementTestTrait
38
{
39
    /**
40
     * A base document that we can reuse in our tests.
41
     *
42
     * @var \DOMDocument|null
43
     */
44
    protected ?DOMDocument $xmlRepresentation = null;
45
46
    /**
47
     * The name of the class we are testing.
48
     *
49
     * @var class-string
1 ignored issue
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
50
     */
51
    protected string $testedClass;
52
53
54
    /**
55
     * Test signing / verifying
56
     */
57
    public function testSignatures(): void
58
    {
59
        if (!class_exists($this->testedClass)) {
60
            $this->markTestSkipped(
0 ignored issues
show
Bug introduced by
It seems like markTestSkipped() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

60
            $this->/** @scrutinizer ignore-call */ 
61
                   markTestSkipped(
Loading history...
61
                'Unable to run ' . self::class . '::testSignatures(). Please set ' . self::class
62
                . ':$testedClass to a class-string representing the XML-class being tested',
63
            );
64
        } elseif (empty($this->xmlRepresentation)) {
65
            $this->markTestSkipped(
66
                'Unable to run ' . self::class . '::testSignatures(). Please set ' . self::class
67
                . ':$xmlRepresentation to a DOMDocument representing the XML-class being tested',
68
            );
69
        } else {
70
            /** @psalm-var class-string|null */
71
            $testedClass = $this->testedClass;
72
73
            /** @psalm-var \DOMElement|null */
74
            $xmlRepresentation = $this->xmlRepresentation;
75
76
            $algorithms = array_keys(C::$RSA_DIGESTS);
77
            foreach ($algorithms as $algorithm) {
78
                if (
79
                    boolval(OPENSSL_VERSION_NUMBER >= hexdec('0x30000000')) === true
80
                    && ($algorithm === C::SIG_RSA_SHA1 || $algorithm === C::SIG_RSA_RIPEMD160)
81
                ) {
82
                    // OpenSSL 3.0 disabled SHA1 and RIPEMD160 support
83
                    continue;
84
                }
85
86
                //
87
                // sign with two certificates
88
                //
89
                $signer = (new SignatureAlgorithmFactory([]))->getAlgorithm(
90
                    $algorithm,
91
                    PEMCertificatesMock::getPrivateKey(PEMCertificatesMock::PRIVATE_KEY),
92
                );
93
94
                $keyInfo = new KeyInfo([
95
                    new X509Data([new X509Certificate(
96
                        PEMCertificatesMock::getPlainPublicKeyContents(PEMCertificatesMock::PUBLIC_KEY),
97
                    )]),
98
                    new X509Data([new X509Certificate(
99
                        PEMCertificatesMock::getPlainPublicKeyContents(PEMCertificatesMock::OTHER_PUBLIC_KEY),
100
                    )]),
101
                ]);
102
103
                $unsigned = $testedClass::fromXML($xmlRepresentation->documentElement);
104
                $unsigned->sign($signer, C::C14N_EXCLUSIVE_WITHOUT_COMMENTS, $keyInfo);
105
                $signed = $this->testedClass::fromXML($unsigned->toXML());
106
                $this->assertEquals(
0 ignored issues
show
Bug introduced by
It seems like assertEquals() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

106
                $this->/** @scrutinizer ignore-call */ 
107
                       assertEquals(
Loading history...
107
                    $algorithm,
108
                    $signed->getSignature()->getSignedInfo()->getSignatureMethod()->getAlgorithm(),
109
                );
110
111
                // verify signature
112
                $verifier = (new SignatureAlgorithmFactory([]))->getAlgorithm(
113
                    $signed->getSignature()->getSignedInfo()->getSignatureMethod()->getAlgorithm(),
114
                    PEMCertificatesMock::getPublicKey(PEMCertificatesMock::PUBLIC_KEY),
115
                );
116
117
                try {
118
                    $verified = $signed->verify($verifier);
119
                } catch (
120
                    NoSignatureFoundException |
121
                    InvalidArgumentException |
122
                    SignatureVerificationFailedException $s
123
                ) {
124
                    $this->fail(sprintf('%s:  %s', $algorithm, $e->getMessage()));
0 ignored issues
show
Bug introduced by
It seems like fail() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

124
                    $this->/** @scrutinizer ignore-call */ 
125
                           fail(sprintf('%s:  %s', $algorithm, $e->getMessage()));
Loading history...
Comprehensibility Best Practice introduced by
The variable $e does not seem to be defined for all execution paths leading up to this point.
Loading history...
125
                }
126
                $this->assertInstanceOf($this->testedClass, $verified);
0 ignored issues
show
Bug introduced by
It seems like assertInstanceOf() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

126
                $this->/** @scrutinizer ignore-call */ 
127
                       assertInstanceOf($this->testedClass, $verified);
Loading history...
127
128
                $this->assertEquals(
129
                    PEMCertificatesMock::getPublicKey(PEMCertificatesMock::PUBLIC_KEY),
130
                    $verified->getVerifyingKey(),
131
                    sprintf('No validating certificate for algorithm: %s', $algorithm),
132
                );
133
134
                //
135
                // sign without certificates
136
                //
137
                $unsigned->sign($signer, C::C14N_EXCLUSIVE_WITHOUT_COMMENTS, null);
138
                $signed = $this->testedClass::fromXML($unsigned->toXML());
139
140
                // verify signature
141
                try {
142
                    $verified = $signed->verify($verifier);
143
                } catch (
144
                    NoSignatureFoundException |
145
                    InvalidArgumentException |
146
                    SignatureVerificationFailedException $e
147
                ) {
148
                    $this->fail(sprintf('%s:  %s', $algorithm, $e->getMessage()));
149
                }
150
                $this->assertInstanceOf($this->testedClass, $verified);
151
152
                $this->assertEquals(
153
                    PEMCertificatesMock::getPublicKey(PEMCertificatesMock::PUBLIC_KEY),
154
                    $verified->getVerifyingKey(),
155
                    sprintf('No validating certificate for algorithm: %s', $algorithm),
156
                );
157
158
                //
159
                // verify with wrong key
160
                //
161
                $signer = (new SignatureAlgorithmFactory([]))->getAlgorithm(
162
                    $algorithm,
163
                    PEMCertificatesMock::getPrivateKey(PEMCertificatesMock::OTHER_PRIVATE_KEY),
164
                );
165
                $unsigned->sign($signer, C::C14N_EXCLUSIVE_WITHOUT_COMMENTS, null);
166
                $signed = $this->testedClass::fromXML($unsigned->toXML());
167
168
                // verify signature
169
                try {
170
                    $verified = $signed->verify($verifier);
171
                    $this->fail('Signature validated correctly with wrong certificate.');
172
                } catch (
173
                    NoSignatureFoundException |
174
                    InvalidArgumentException |
175
                    SignatureVerificationFailedException $e
176
                ) {
177
                    $this->assertEquals('Failed to verify signature.', $e->getMessage());
178
                }
179
                $this->assertInstanceOf($this->testedClass, $verified);
180
181
                $this->assertEquals(
182
                    PEMCertificatesMock::getPublicKey(PEMCertificatesMock::PUBLIC_KEY),
183
                    $verified->getVerifyingKey(),
184
                    sprintf('No validating certificate for algorithm: %s', $algorithm),
185
                );
186
            }
187
        }
188
    }
189
}
190