Passed
Branch refactoring (2fa5c6)
by Fabian
14:03
created

AbstractAuthentication::generateCnonce()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 7
nc 5
nop 0
dl 0
loc 14
rs 9.6111
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Sasl library.
7
 *
8
 * Copyright (c) 2002-2003 Richard Heyes,
9
 *               2014-2025 Fabian Grutschus
10
 * All rights reserved.
11
 *
12
 * Redistribution and use in source and binary forms, with or without
13
 * modification, are permitted provided that the following conditions
14
 * are met:
15
 *
16
 * o Redistributions of source code must retain the above copyright
17
 *   notice, this list of conditions and the following disclaimer.
18
 * o Redistributions in binary form must reproduce the above copyright
19
 *   notice, this list of conditions and the following disclaimer in the
20
 *   documentation and/or other materials provided with the distribution.|
21
 * o The names of the authors may not be used to endorse or promote
22
 *   products derived from this software without specific prior written
23
 *   permission.
24
 *
25
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36
 *
37
 * @author Richard Heyes <[email protected]>
38
 */
39
40
namespace Fabiang\SASL\Authentication;
41
42
use Fabiang\SASL\Options;
43
44
/**
45
 * Common functionality to SASL mechanisms
46
 *
47
 * @author Richard Heyes <[email protected]>
48
 */
49
abstract class AbstractAuthentication
50
{
51
    /**
52
     * Use random devices.
53
     */
54
    public static bool $useDevRandom = true;
55
56
    private static array $randomDevices = ['/dev/urandom', '/dev/random'];
57
58
    /**
59
     * Options object.
60
     */
61
    protected Options $options;
62
63
    public function __construct(Options $options)
64
    {
65
        $this->options = $options;
66
    }
67
68
    /**
69
     * Get options object.
70
     */
71
    public function getOptions(): Options
72
    {
73
        return $this->options;
74
    }
75
76
    /**
77
     * Creates the client nonce for the response
78
     *
79
     * @return string The cnonce value
80
     */
81
    protected function generateCnonce(): string
82
    {
83
        foreach (self::$randomDevices as $file) {
84
            if (true === static::$useDevRandom && is_readable($file)) {
85
                return base64_encode(file_get_contents($file, false, null, 0, 32));
86
            }
87
        }
88
89
        $cnonce = '';
90
        for ($i = 0; $i < 32; $i++) {
91
            $cnonce .= chr(mt_rand(0, 255));
92
        }
93
94
        return base64_encode($cnonce);
95
    }
96
97
    /**
98
     * Generate downgrade protection string
99
     */
100
    protected function generateDowngradeProtectionVerification(): string
101
    {
102
        $downgradeProtectionOptions = $this->options->getDowngradeProtection();
103
104
        $allowedMechanisms      = $downgradeProtectionOptions->getAllowedMechanisms();
105
        $allowedChannelBindings = $downgradeProtectionOptions->getAllowedChannelBindings();
106
107
        if (count($allowedMechanisms) === 0 && count($allowedChannelBindings) === 0) {
108
            return '';
109
        }
110
111
        usort($allowedMechanisms, [$this, 'sortOctetCollation']);
112
        usort($allowedChannelBindings, [$this, 'sortOctetCollation']);
113
114
        $protect = implode(',', $allowedMechanisms);
115
        if (count($allowedChannelBindings) > 0) {
116
            $protect .= '|' . implode(',', $allowedChannelBindings);
117
        }
118
        return $protect;
119
    }
120
121
    /**
122
     * @link https://datatracker.ietf.org/doc/html/rfc4790#page-22
123
     */
124
    private function sortOctetCollation(string $a, string $b): int
125
    {
126
        if ($a == $b) {
127
            return 0;
128
        }
129
        return ($a < $b) ? -1 : 1;
130
    }
131
}
132