Passed
Push — master ( c0a3a7...3b84a4 )
by Jeroen
58:51
created

SiteSecret::getStrength()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 8
nc 4
nop 0
dl 0
loc 12
rs 9.2
c 0
b 0
f 0
ccs 0
cts 9
cp 0
crap 20
1
<?php
2
namespace Elgg\Database;
3
4
use Elgg\Config as ElggConfig;
5
use Elgg\Database;
6
use ElggCrypto;
7
8
/**
9
 * Manages a site-specific secret key, encoded as a 32 byte string "secret"
10
 *
11
 * The key can have two formats:
12
 *   - Since 1.8.17 all keys generated are Base64URL-encoded with the 1st character set to "z" so that
13
 *     the format can be recognized. With one character lost, this makes the keys effectively 186 bits.
14
 *   - Before 1.8.17 keys were hex-encoded (128 bits) but created from insufficiently random sources.
15
 *
16
 * The hex keys were created with rand() as the only decent source of entropy (the site's creation time
17
 * is not too difficult to find). As such, systems with a low getrandmax() value created particularly
18
 * weak keys. You can check key string using getStrength().
19
 *
20
 * @access private
21
 * @since  1.10.0
22
 */
23
class SiteSecret {
24
25
	const CONFIG_KEY = '__site_secret__';
26
27
	/**
28
	 * Constructor
29
	 *
30
	 * @param string $key Site key (32 hex chars, or "z" and 31 base64 chars)
31
	 */
32 4777
	public function __construct($key) {
33 4777
		$this->key = $key;
34 4777
	}
35
36
	/**
37
	 * @var string
38
	 */
39
	private $key;
40
41
	/**
42
	 * Returns the site secret.
43
	 *
44
	 * Used to generate difficult to guess hashes for sessions and action tokens.
45
	 *
46
	 * @param bool $raw If true, a binary key will be returned
47
	 *
48
	 * @return string Site secret.
49
	 * @access private
50
	 */
51 88
	public function get($raw = false) {
52 88
		if (!$this->key) {
53
			throw new \RuntimeException('Secret key is not set');
54
		}
55
56 88
		if (!$raw) {
57 4
			return $this->key;
58
		}
59
60
		// try to return binary key
61 84
		if ($this->key[0] === 'z') {
62
			// new keys are "z" + base64URL
63 84
			$base64 = strtr(substr($this->key, 1), '-_', '+/');
64 84
			$key = base64_decode($base64);
65 84
			if ($key !== false) {
66 84
				return $key;
67
			}
68
69
			// on failure, at least return string key :/
70
			return $this->key;
71
		}
72
73
		// old keys are hex
74
		return hex2bin($this->key);
75
	}
76
77
	/**
78
	 * Get the strength of the site secret
79
	 *
80
	 * If "weak" or "moderate" is returned, this assumes we're running on the same system that created
81
	 * the key.
82
	 *
83
	 * @return string "strong", "moderate", or "weak"
84
	 * @access private
85
	 */
86
	public function getStrength() {
87
		$secret = $this->get();
88
		if ($secret[0] !== 'z') {
89
			$rand_max = getrandmax();
90
			if ($rand_max < pow(2, 16)) {
91
				return 'weak';
92
			}
93
			if ($rand_max < pow(2, 32)) {
94
				return 'moderate';
95
			}
96
		}
97
		return 'strong';
98
	}
99
100
	/**
101
	 * Initialise the site secret (32 bytes: "z" to indicate format + 186-bit key in Base64 URL)
102
	 * and save to config table.
103
	 *
104
	 * Used during installation or regeneration.
105
	 *
106
	 * @param ElggCrypto  $crypto Crypto service
107
	 * @param ConfigTable $table  Config table
108
	 * @return SiteSecret
109
	 */
110 2
	public static function regenerate(ElggCrypto $crypto, ConfigTable $table) {
111 2
		$key = 'z' . $crypto->getRandomString(31);
112
113 2
		$table->set(self::CONFIG_KEY, $key);
114
115 2
		return new self($key);
116
	}
117
118
	/**
119
	 * Create from config/storage.
120
	 *
121
	 * @param ConfigTable $table Config table
122
	 *
123
	 * @return SiteSecret
124
	 * @throws \InstallationException
125
	 */
126 1
	public static function fromDatabase(ConfigTable $table) {
127 1
		$key = $table->get(self::CONFIG_KEY);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $key is correct as $table->get(self::CONFIG_KEY) targeting Elgg\Database\ConfigTable::get() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
128 1
		if (!$key) {
129
			throw new \InstallationException('Site secret is not in the config table.');
130
		}
131
132 1
		return new self($key);
0 ignored issues
show
Bug introduced by
$key of type void is incompatible with the type string expected by parameter $key of Elgg\Database\SiteSecret::__construct(). ( Ignorable by Annotation )

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

132
		return new self(/** @scrutinizer ignore-type */ $key);
Loading history...
133
	}
134
135
	/**
136
	 * Create from a config value. If successful, the value will be erased from config.
137
	 *
138
	 * @param ElggConfig $config Config
139
	 *
140
	 * @return SiteSecret|false
141
	 */
142 4777
	public static function fromConfig(ElggConfig $config) {
143 4777
		$key = $config->{self::CONFIG_KEY};
144 4777
		if (!$key) {
145 4417
			return false;
146
		}
147
148
		// Don't leave this sitting around in config, in case it gets dumped
149 4777
		unset($config->{self::CONFIG_KEY});
150
151 4777
		return new self($key);
152
	}
153
}
154