Skip to content

Commit 0d4d27c

Browse files
authored
Merge pull request #398 from defuse/make-throw-ifs-an-assert
Improve Test Coverage
2 parents 39fba0c + d2b04d2 commit 0d4d27c

File tree

12 files changed

+308
-196
lines changed

12 files changed

+308
-196
lines changed

src/Core.php

Lines changed: 55 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -50,29 +50,26 @@ final class Core
5050
*/
5151
public static function incrementCounter($ctr, $inc)
5252
{
53-
if (Core::ourStrlen($ctr) !== Core::BLOCK_BYTE_SIZE) {
54-
throw new Ex\EnvironmentIsBrokenException(
55-
'Trying to increment a nonce of the wrong size.'
56-
);
57-
}
58-
59-
if (! \is_int($inc)) {
60-
throw new Ex\EnvironmentIsBrokenException(
61-
'Trying to increment nonce by a non-integer.'
62-
);
63-
}
64-
65-
if ($inc < 0) {
66-
throw new Ex\EnvironmentIsBrokenException(
67-
'Trying to increment nonce by a negative amount.'
68-
);
69-
}
70-
71-
if ($inc > PHP_INT_MAX - 255) {
72-
throw new Ex\EnvironmentIsBrokenException(
73-
'Integer overflow may occur.'
74-
);
75-
}
53+
Core::ensureTrue(
54+
Core::ourStrlen($ctr) === Core::BLOCK_BYTE_SIZE,
55+
'Trying to increment a nonce of the wrong size.'
56+
);
57+
58+
Core::ensureTrue(
59+
\is_int($inc),
60+
'Trying to increment nonce by a non-integer.'
61+
);
62+
63+
// The caller is probably re-using CTR-mode keystream if they increment by 0.
64+
Core::ensureTrue(
65+
$inc > 0,
66+
'Trying to increment a nonce by a nonpositive amount'
67+
);
68+
69+
Core::ensureTrue(
70+
$inc <= PHP_INT_MAX - 255,
71+
'Integer overflow may occur'
72+
);
7673

7774
/*
7875
* We start at the rightmost byte (big-endian)
@@ -82,11 +79,7 @@ public static function incrementCounter($ctr, $inc)
8279
$sum = \ord($ctr[$i]) + $inc;
8380

8481
/* Detect integer overflow and fail. */
85-
if (! \is_int($sum)) {
86-
throw new Ex\EnvironmentIsBrokenException(
87-
'Integer overflow in CTR mode nonce increment.'
88-
);
89-
}
82+
Core::ensureTrue(\is_int($sum), 'Integer overflow in CTR mode nonce increment');
9083

9184
$ctr[$i] = \pack('C', $sum & 0xFF);
9285
$inc = $sum >> 8;
@@ -146,12 +139,10 @@ public static function HKDF($hash, $ikm, $length, $info = '', $salt = null)
146139
$digest_length = Core::ourStrlen(\hash_hmac($hash, '', '', true));
147140

148141
// Sanity-check the desired output length.
149-
if (empty($length) || ! \is_int($length) ||
150-
$length < 0 || $length > 255 * $digest_length) {
151-
throw new Ex\EnvironmentIsBrokenException(
152-
'Bad output length requested of HKDF.'
153-
);
154-
}
142+
Core::ensureTrue(
143+
!empty($length) && \is_int($length) && $length >= 0 && $length <= 255 * $digest_length,
144+
'Bad output length requested of HDKF.'
145+
);
155146

156147
// "if [salt] not provided, is set to a string of HashLen zeroes."
157148
if (\is_null($salt)) {
@@ -166,9 +157,7 @@ public static function HKDF($hash, $ikm, $length, $info = '', $salt = null)
166157
// HKDF-Expand:
167158

168159
// This check is useless, but it serves as a reminder to the spec.
169-
if (Core::ourStrlen($prk) < $digest_length) {
170-
throw new Ex\EnvironmentIsBrokenException();
171-
}
160+
Core::ensureTrue(Core::ourStrlen($prk) >= $digest_length);
172161

173162
// T(0) = ''
174163
$t = '';
@@ -188,9 +177,7 @@ public static function HKDF($hash, $ikm, $length, $info = '', $salt = null)
188177
// ORM = first L octets of T
189178
/** @var string $orm */
190179
$orm = Core::ourSubstr($t, 0, $length);
191-
if (!\is_string($orm)) {
192-
throw new Ex\EnvironmentIsBrokenException();
193-
}
180+
Core::ensureTrue(\is_string($orm));
194181
return $orm;
195182
}
196183

@@ -224,9 +211,7 @@ public static function hashEquals($expected, $given)
224211
// We're not attempting to make variable-length string comparison
225212
// secure, as that's very difficult. Make sure the strings are the same
226213
// length.
227-
if (Core::ourStrlen($expected) !== Core::ourStrlen($given)) {
228-
throw new Ex\EnvironmentIsBrokenException();
229-
}
214+
Core::ensureTrue(Core::ourStrlen($expected) === Core::ourStrlen($given));
230215

231216
$blind = Core::secureRandom(32);
232217
$message_compare = \hash_hmac(Core::HASH_FUNCTION_NAME, $given, $blind);
@@ -243,9 +228,7 @@ public static function hashEquals($expected, $given)
243228
*/
244229
public static function ensureConstantExists($name)
245230
{
246-
if (! \defined($name)) {
247-
throw new Ex\EnvironmentIsBrokenException();
248-
}
231+
Core::ensureTrue(\defined($name));
249232
}
250233

251234
/**
@@ -258,8 +241,22 @@ public static function ensureConstantExists($name)
258241
*/
259242
public static function ensureFunctionExists($name)
260243
{
261-
if (! \function_exists($name)) {
262-
throw new Ex\EnvironmentIsBrokenException();
244+
Core::ensureTrue(\function_exists($name));
245+
}
246+
247+
/**
248+
* Throws an exception if the condition is false.
249+
*
250+
* @param bool $condition
251+
* @param string $message
252+
* @return void
253+
*
254+
* @throws Ex\EnvironmentIsBrokenException
255+
*/
256+
public static function ensureTrue($condition, $message = '')
257+
{
258+
if (!$condition) {
259+
throw new Ex\EnvironmentIsBrokenException($message);
263260
}
264261
}
265262

@@ -286,9 +283,7 @@ public static function ourStrlen($str)
286283
}
287284
if ($exists) {
288285
$length = \mb_strlen($str, '8bit');
289-
if ($length === false) {
290-
throw new Ex\EnvironmentIsBrokenException();
291-
}
286+
Core::ensureTrue($length !== false);
292287
return $length;
293288
} else {
294289
return \strlen($str);
@@ -403,28 +398,22 @@ public static function pbkdf2($algorithm, $password, $salt, $count, $key_length,
403398
$key_length += 0;
404399

405400
$algorithm = \strtolower($algorithm);
406-
if (! \in_array($algorithm, \hash_algos(), true)) {
407-
throw new Ex\EnvironmentIsBrokenException(
408-
'Invalid or unsupported hash algorithm.'
409-
);
410-
}
401+
Core::ensureTrue(
402+
\in_array($algorithm, \hash_algos(), true),
403+
'Invalid or unsupported hash algorithm.'
404+
);
411405

412406
// Whitelist, or we could end up with people using CRC32.
413407
$ok_algorithms = [
414408
'sha1', 'sha224', 'sha256', 'sha384', 'sha512',
415409
'ripemd160', 'ripemd256', 'ripemd320', 'whirlpool',
416410
];
417-
if (! \in_array($algorithm, $ok_algorithms, true)) {
418-
throw new Ex\EnvironmentIsBrokenException(
419-
'Algorithm is not a secure cryptographic hash function.'
420-
);
421-
}
411+
Core::ensureTrue(
412+
\in_array($algorithm, $ok_algorithms, true),
413+
'Algorithm is not a secure cryptographic hash function.'
414+
);
422415

423-
if ($count <= 0 || $key_length <= 0) {
424-
throw new Ex\EnvironmentIsBrokenException(
425-
'Invalid PBKDF2 parameters.'
426-
);
427-
}
416+
Core::ensureTrue($count > 0 && $key_length > 0, 'Invalid PBKDF2 parameters.');
428417

429418
if (\function_exists('hash_pbkdf2')) {
430419
// The output length is in NIBBLES (4-bits) if $raw_output is false!

src/Crypto.php

Lines changed: 22 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,18 @@ class Crypto
1818
*
1919
* @return string
2020
*/
21-
public static function encrypt($plaintext, Key $key, $raw_binary = false)
21+
public static function encrypt($plaintext, $key, $raw_binary = false)
2222
{
2323
if (!\is_string($plaintext)) {
2424
throw new \TypeError(
2525
'String expected for argument 1. ' . \ucfirst(\gettype($plaintext)) . ' given instead.'
2626
);
2727
}
28+
if (!($key instanceof Key)) {
29+
throw new \TypeError(
30+
'Key expected for argument 2. ' . \ucfirst(\gettype($key)) . ' given instead.'
31+
);
32+
}
2833
if (!\is_bool($raw_binary)) {
2934
throw new \TypeError(
3035
'Boolean expected for argument 3. ' . \ucfirst(\gettype($raw_binary)) . ' given instead.'
@@ -87,13 +92,18 @@ public static function encryptWithPassword($plaintext, $password, $raw_binary =
8792
*
8893
* @return string
8994
*/
90-
public static function decrypt($ciphertext, Key $key, $raw_binary = false)
95+
public static function decrypt($ciphertext, $key, $raw_binary = false)
9196
{
9297
if (!\is_string($ciphertext)) {
9398
throw new \TypeError(
9499
'String expected for argument 1. ' . \ucfirst(\gettype($ciphertext)) . ' given instead.'
95100
);
96101
}
102+
if (!($key instanceof Key)) {
103+
throw new \TypeError(
104+
'Key expected for argument 2. ' . \ucfirst(\gettype($key)) . ' given instead.'
105+
);
106+
}
97107
if (!\is_bool($raw_binary)) {
98108
throw new \TypeError(
99109
'Boolean expected for argument 3. ' . \ucfirst(\gettype($raw_binary)) . ' given instead.'
@@ -181,16 +191,12 @@ public static function legacyDecrypt($ciphertext, $key)
181191
* @var string
182192
*/
183193
$hmac = Core::ourSubstr($ciphertext, 0, Core::LEGACY_MAC_BYTE_SIZE);
184-
if (!\is_string($hmac)) {
185-
throw new Ex\EnvironmentIsBrokenException();
186-
}
194+
Core::ensureTrue(\is_string($hmac));
187195
/**
188196
* @var string
189197
*/
190198
$messageCiphertext = Core::ourSubstr($ciphertext, Core::LEGACY_MAC_BYTE_SIZE);
191-
if (!\is_string($messageCiphertext)) {
192-
throw new Ex\EnvironmentIsBrokenException();
193-
}
199+
Core::ensureTrue(\is_string($messageCiphertext));
194200

195201
// Regenerate the same authentication sub-key.
196202
$akey = Core::HKDF(
@@ -221,17 +227,13 @@ public static function legacyDecrypt($ciphertext, $key)
221227
* @var string
222228
*/
223229
$iv = Core::ourSubstr($messageCiphertext, 0, Core::LEGACY_BLOCK_BYTE_SIZE);
224-
if (!\is_string($iv)) {
225-
throw new Ex\EnvironmentIsBrokenException();
226-
}
230+
Core::ensureTrue(\is_string($iv));
227231

228232
/**
229233
* @var string
230234
*/
231235
$actualCiphertext = Core::ourSubstr($messageCiphertext, Core::LEGACY_BLOCK_BYTE_SIZE);
232-
if (!\is_string($actualCiphertext)) {
233-
throw new Ex\EnvironmentIsBrokenException();
234-
}
236+
Core::ensureTrue(\is_string($actualCiphertext));
235237

236238
// Do the decryption.
237239
$plaintext = self::plainDecrypt($actualCiphertext, $ekey, $iv, Core::LEGACY_CIPHER_METHOD);
@@ -320,9 +322,7 @@ private static function decryptInternal($ciphertext, KeyOrPassword $secret, $raw
320322
Core::HEADER_VERSION_SIZE,
321323
Core::SALT_BYTE_SIZE
322324
);
323-
if (!\is_string($salt)) {
324-
throw new Ex\EnvironmentIsBrokenException();
325-
}
325+
Core::ensureTrue(\is_string($salt));
326326

327327
// Get the IV.
328328
/** @var string $iv */
@@ -331,9 +331,7 @@ private static function decryptInternal($ciphertext, KeyOrPassword $secret, $raw
331331
Core::HEADER_VERSION_SIZE + Core::SALT_BYTE_SIZE,
332332
Core::BLOCK_BYTE_SIZE
333333
);
334-
if (!\is_string($iv)) {
335-
throw new Ex\EnvironmentIsBrokenException();
336-
}
334+
Core::ensureTrue(\is_string($iv));
337335

338336
// Get the HMAC.
339337
/** @var string $hmac */
@@ -342,9 +340,7 @@ private static function decryptInternal($ciphertext, KeyOrPassword $secret, $raw
342340
Core::ourStrlen($ciphertext) - Core::MAC_BYTE_SIZE,
343341
Core::MAC_BYTE_SIZE
344342
);
345-
if (!\is_string($hmac)) {
346-
throw new Ex\EnvironmentIsBrokenException();
347-
}
343+
Core::ensureTrue(\is_string($hmac));
348344

349345
// Get the actual encrypted ciphertext.
350346
/** @var string $encrypted */
@@ -355,9 +351,7 @@ private static function decryptInternal($ciphertext, KeyOrPassword $secret, $raw
355351
Core::ourStrlen($ciphertext) - Core::MAC_BYTE_SIZE - Core::SALT_BYTE_SIZE -
356352
Core::BLOCK_BYTE_SIZE - Core::HEADER_VERSION_SIZE
357353
);
358-
if (!\is_string($encrypted)) {
359-
throw new Ex\EnvironmentIsBrokenException();
360-
}
354+
Core::ensureTrue(\is_string($encrypted));
361355

362356
// Derive the separate encryption and authentication keys from the key
363357
// or password, whichever it is.
@@ -397,11 +391,7 @@ protected static function plainEncrypt($plaintext, $key, $iv)
397391
$iv
398392
);
399393

400-
if (!\is_string($ciphertext)) {
401-
throw new Ex\EnvironmentIsBrokenException(
402-
'openssl_encrypt() failed.'
403-
);
404-
}
394+
Core::ensureTrue(\is_string($ciphertext), 'openssl_encrypt() failed');
405395

406396
return $ciphertext;
407397
}
@@ -431,11 +421,7 @@ protected static function plainDecrypt($ciphertext, $key, $iv, $cipherMethod)
431421
OPENSSL_RAW_DATA,
432422
$iv
433423
);
434-
if (!\is_string($plaintext)) {
435-
throw new Ex\EnvironmentIsBrokenException(
436-
'openssl_decrypt() failed.'
437-
);
438-
}
424+
Core::ensureTrue(\is_string($plaintext), 'openssl_decrypt() failed.');
439425

440426
return $plaintext;
441427
}

src/Encoding.php

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -178,11 +178,10 @@ public static function saveBytesToChecksummedAsciiSafeString($header, $bytes)
178178
{
179179
// Headers must be a constant length to prevent one type's header from
180180
// being a prefix of another type's header, leading to ambiguity.
181-
if (Core::ourStrlen($header) !== self::SERIALIZE_HEADER_BYTES) {
182-
throw new Ex\EnvironmentIsBrokenException(
183-
'Header must be ' . self::SERIALIZE_HEADER_BYTES . ' bytes.'
184-
);
185-
}
181+
Core::ensureTrue(
182+
Core::ourStrlen($header) === self::SERIALIZE_HEADER_BYTES,
183+
'Header must be ' . self::SERIALIZE_HEADER_BYTES . ' bytes.'
184+
);
186185

187186
return Encoding::binToHex(
188187
$header .
@@ -211,11 +210,10 @@ public static function loadBytesFromChecksummedAsciiSafeString($expected_header,
211210
{
212211
// Headers must be a constant length to prevent one type's header from
213212
// being a prefix of another type's header, leading to ambiguity.
214-
if (Core::ourStrlen($expected_header) !== self::SERIALIZE_HEADER_BYTES) {
215-
throw new Ex\EnvironmentIsBrokenException(
216-
'Header must be 4 bytes.'
217-
);
218-
}
213+
Core::ensureTrue(
214+
Core::ourStrlen($expected_header) === self::SERIALIZE_HEADER_BYTES,
215+
'Header must be 4 bytes.'
216+
);
219217

220218
/* If you get an exception here when attempting to load from a file, first pass your
221219
key to Encoding::trimTrailingWhitespace() to remove newline characters, etc. */

0 commit comments

Comments
 (0)