Passed
Branch refactoring (9be877)
by Fabian
16:03
created

AbstractAuthentication::sortOctetCollation()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 3
nc 3
nop 2
dl 0
loc 6
rs 10
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
    private static array $randomDevices = ['/dev/urandom', '/dev/random'];
56
57
    public function __construct(protected Options $options)
58
    {
59
        $this->options = $options;
60
    }
61
62
    /**
63
     * Get options object.
64
     */
65
    public function getOptions(): Options
66
    {
67
        return $this->options;
68
    }
69
70
    /**
71
     * Creates the client nonce for the response
72
     *
73
     * @return string The cnonce value
74
     */
75
    protected function generateCnonce(): string
76
    {
77
        foreach (self::$randomDevices as $file) {
78
            if (true === static::$useDevRandom && is_readable($file)) {
79
                $rand = file_get_contents($file, false, null, 0, 32);
80
                if ($rand !== false) {
81
                    return base64_encode($rand);
82
                }
83
            }
84
        }
85
86
        $cnonce = '';
87
        for ($i = 0; $i < 32; $i++) {
88
            $cnonce .= chr(mt_rand(0, 255));
89
        }
90
91
        return base64_encode($cnonce);
92
    }
93
94
    /**
95
     * Generate downgrade protection string
96
     */
97
    protected function generateDowngradeProtectionVerification(): string
98
    {
99
        $downgradeProtectionOptions = $this->options->getDowngradeProtection();
100
        if ($downgradeProtectionOptions === null) {
101
            return '';
102
        }
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