Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Phalcon\Security\Random:base62 #12858

Merged
merged 1 commit into from
May 21, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
- Added a new `Phalcon\Mvc\Model\Binder::findBoundModel` method. Params fetched from cache are being added to `internalCache` class property in `Phalcon\Mvc\Model\Binder::getParamsFromCache`
- Added `Phalcon\Mvc\Model\Criteria::createBuilder` to create a query builder from criteria
- Added `dispatcher::beforeForward` event to allow forwarding request to the separated module [#121](https://github.com/phalcon/cphalcon/issues/121), [#12417](https://github.com/phalcon/cphalcon/issues/12417)
- Added `Phalcon\Security\Random:base62` to provide the largest value that can safely be used in URLs without needing to take extra characters into consideration [#12105](https://github.com/phalcon/cphalcon/issues/12105)
- Fixed Dispatcher forwarding when handling exception [#11819](https://github.com/phalcon/cphalcon/issues/11819), [#12154](https://github.com/phalcon/cphalcon/issues/12154)
- Fixed params view scope for PHP 7 [#12648](https://github.com/phalcon/cphalcon/issues/12648)
- Fixed `Phalcon\Mvc\Micro::handle` to prevent attemps to send response twice [#12668](https://github.com/phalcon/cphalcon/pull/12668)
Expand Down
81 changes: 59 additions & 22 deletions phalcon/security/random.zep
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ namespace Phalcon\Security;
* echo $random->hex(12); // 95469d667475125208be45c4
* echo $random->hex(13); // 05475e8af4a34f8f743ab48761
*
* // Random base62 string
* echo $random->base62(); // z0RkwHfh8ErDM1xw
*
* // Random base64 string
* echo $random->base64(12); // XfIN81jGGuKkcE1E
* echo $random->base64(12); // 3rcq39QzGK9fUqh8
Expand Down Expand Up @@ -172,45 +175,52 @@ class Random
* If $len is not specified, 16 is assumed. It may be larger in future.
* The result may contain alphanumeric characters except 0, O, I and l.
*
* It is similar to Base64 but has been modified to avoid both non-alphanumeric
* It is similar to `Phalcon\Security\Random:base64` but has been modified to avoid both non-alphanumeric
* characters and letters which might look ambiguous when printed.
*
*<code>
* <code>
* $random = new \Phalcon\Security\Random();
*
* echo $random->base58(); // 4kUgL2pdQMSCQtjE
*</code>
* </code>
*
* @link https://en.wikipedia.org/wiki/Base58
* @see \Phalcon\Security\Random:base64
* @link https://en.wikipedia.org/wiki/Base58
* @throws Exception If secure random number generator is not available or unexpected partial read
*/
public function base58(n = null) -> string
public function base58(int len = null) -> string
{
var bytes, idx;
string byteString = "",
alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";

let bytes = unpack("C*", this->bytes(n));

for idx in bytes {
let idx = idx % 64;

if idx >= 58 {
let idx = this->number(57);
}

let byteString .= alphabet[(int) idx];
}
return this->base("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz", 58, len);
}

return byteString;
/**
* Generates a random base62 string
*
* If $len is not specified, 16 is assumed. It may be larger in future.
*
* It is similar to `Phalcon\Security\Random:base58` but has been modified to provide the largest value that can
* safely be used in URLs without needing to take extra characters into consideration because it is [A-Za-z0-9].
*
*< code>
* $random = new \Phalcon\Security\Random();
*
* echo $random->base62(); // z0RkwHfh8ErDM1xw
* </code>
*
* @see \Phalcon\Security\Random:base58
* @throws Exception If secure random number generator is not available or unexpected partial read
*/
public function base62(int len = null) -> string
{
return this->base("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 62, len);
}

/**
* Generates a random base64 string
*
* If $len is not specified, 16 is assumed. It may be larger in future.
* The length of the result string is usually greater of $len.
* Size formula: 4 *( $len / 3) and this need to be rounded up to a multiple of 4.
* Size formula: 4 * ($len / 3) and this need to be rounded up to a multiple of 4.
*
*<code>
* $random = new \Phalcon\Security\Random();
Expand Down Expand Up @@ -343,4 +353,31 @@ class Random

return hexdec(array_shift(ret));
}

/**
* Generates a random string based on the number ($base) of characters ($alphabet).
*
* If $n is not specified, 16 is assumed. It may be larger in future.
*
* @throws Exception If secure random number generator is not available or unexpected partial read
*/
protected function base(string alphabet, int base, n = null) -> string
{
var bytes, idx;
string byteString = "";

let bytes = unpack("C*", this->bytes(n));

for idx in bytes {
let idx = idx % 64;

if idx >= base {
let idx = this->number(base - 1);
}

let byteString .= alphabet[(int) idx];
}

return byteString;
}
}
180 changes: 118 additions & 62 deletions tests/unit/Security/RandomTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,16 +106,7 @@ public function testRandomBase58()
{
$this->specify(
"base58 does not generate a valid string",
function () {
$lens = [
2,
12,
16,
24,
48,
100
];

function ($len) {
$random = new Random();

$isValid = function ($base58) {
Expand All @@ -131,17 +122,69 @@ function () {
return (preg_match('#^[^'.join('', $alphabet).']+$#i', $base58) === 0);
};

foreach ($lens as $len) {
$actual = $random->base58($len);
$actual = $random->base58($len);

if ($len === null) {
expect(strlen($actual))->equals(16);
} else {
expect(strlen($actual))->equals($len);
expect($isValid($actual))->true();
}

$actual = $random->base58();
expect(strlen($actual))->equals(16);
expect($isValid($actual))->true();
}
},
[
'examples' => [
[null],
[2],
[12],
[16],
[24],
[48],
[100],
]
]
);
}

/**
* Tests the random base62 generation
*
* @issue 12105
* @author Serghei Iakovlev <serghei@phalconphp.com>
* @since 2017-05-21
*/
public function testRandomBase62()
{
$this->specify(
'base62 does not generate a valid string',
function ($len) {
$random = new Random();

$isValid = function ($base62) {
return (preg_match("#^[^a-z0-9]+$#i", $base62) === 0);
};

$actual = $random->base62($len);

if ($len === null) {
expect(strlen($actual))->equals(16);
} else {
expect(strlen($actual))->equals($len);
}

expect($isValid($actual))->true();
},
[
'examples' => [
[null],
[2],
[12],
[16],
[24],
[48],
[100],
]
]
);
}

Expand All @@ -155,40 +198,34 @@ public function testRandomBase64()
{
$this->specify(
"base64 does not generate a valid string",
function () {
$lens = [
2,
12,
16,
24,
48,
100
];

function ($len) {
$random = new Random();

$checkSize = function ($base64, $len) {
// Size formula: 4 *( $len / 3) and this need to be rounded up to a multiple of 4.
$formula = (round(4*($len/3))%4 === 0) ? round(4*($len/3)) : round((4*($len/3)+4/2)/4)*4;

return strlen($base64) == $formula;
};

$isValid = function ($base64) {
return (preg_match("#[^a-z0-9+_=/-]+#i", $base64) === 0);
};

foreach ($lens as $len) {
$actual = $random->base64($len);
$actual = $random->base64($len);

expect($checkSize($actual, $len))->true();
expect($isValid($actual))->true();
if ($len === null) {
expect($this->checkSize($actual, 16))->true();
} else {
expect($this->checkSize($actual, $len))->true();
}

$actual = $random->base64();
expect($checkSize($actual, 16))->true();
expect($isValid($actual))->true();
}
},
[
'examples' => [
[null],
[2],
[12],
[16],
[24],
[48],
[100],
]
]
);
}

Expand All @@ -202,34 +239,34 @@ public function testRandomBase64Safe()
{
$this->specify(
"base64Safe does not generate a valid string",
function () {
$lens = [
2,
12,
16,
24,
48,
100
];

function ($len, $padding, $pattern) {
$random = new Random();

$isValid = function ($base64, $padding = false) {
$pattern = $padding ? "a-z0-9_=-" : "a-z0-9_-";
$isValid = function ($base64) use ($pattern) {
return (preg_match("#[^$pattern]+#i", $base64) === 0);
};

foreach ($lens as $len) {
$actual = $random->base64Safe($len);
expect($isValid($actual))->true();
}

$actual = $random->base64Safe();
$actual = $random->base64Safe($len, $padding);
expect($isValid($actual))->true();

$actual = $random->base64Safe(null, true);
expect($isValid($actual, true))->true();
}
},
[
'examples' => [
[null, false, 'a-z0-9_-' ],
[null, true, 'a-z0-9_=-'],
[2, false, 'a-z0-9_-' ],
[2, true, 'a-z0-9_=-'],
[12, false, 'a-z0-9_-' ],
[12, true, 'a-z0-9_=-'],
[16, false, 'a-z0-9_-' ],
[16, true, 'a-z0-9_=-'],
[24, false, 'a-z0-9_-' ],
[24, true, 'a-z0-9_=-'],
[48, false, 'a-z0-9_-' ],
[48, true, 'a-z0-9_=-'],
[100, false, 'a-z0-9_-' ],
[100, true, 'a-z0-9_=-'],
]
]
);
}

Expand Down Expand Up @@ -317,4 +354,23 @@ function () {
}
);
}

/**
* Size formula: 4 * ($n / 3) and this need to be rounded up to a multiple of 4.
*
* @param string $string
* @param int $n
*
* @return bool
*/
protected function checkSize($string, $n)
{
if (round(4 * ($n / 3)) % 4 === 0) {
$len = round(4 * ($n / 3));
} else {
$len = round((4 * ($n / 3) + 4 / 2) / 4) * 4;
}

return strlen($string) == $len;
}
}