Skip to content
This repository has been archived by the owner on Jan 29, 2020. It is now read-only.

Commit

Permalink
Merge branch 'security/zf2015-10'
Browse files Browse the repository at this point in the history
ZF2015-10 patch
  • Loading branch information
weierophinney committed Nov 23, 2015
2 parents 4c3472d + 9b6de1f commit b7a5f99
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 17 deletions.
85 changes: 85 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Changelog

All notable changes to this project will be documented in this file, in reverse chronological order by release.

## 2.5.2 - 2015-11-23

### Added

- Nothing.

### Deprecated

- Nothing.

### Removed

- Nothing.

### Fixed

- **ZF2015-10**: `Zend\Crypt\PublicKey\Rsa\PublicKey` has a call to `openssl_public_encrypt()`
which used PHP's default `$padding` argument, which specifies
`OPENSSL_PKCS1_PADDING`, indicating usage of PKCS1v1.5 padding. This padding
has a known vulnerability, the
[Bleichenbacher's chosen-ciphertext attack](http://crypto.stackexchange.com/questions/12688/can-you-explain-bleichenbachers-cca-attack-on-pkcs1-v1-5),
which can be used to recover an RSA private key. This release contains a patch
that changes the padding argument to use `OPENSSL_PKCS1_OAEP_PADDING`.

Users upgrading to this version may have issues decrypting previously stored
values, due to the change in padding. If this occurs, you can pass the
constant `OPENSSL_PKCS1_PADDING` to a new `$padding` argument in
`Zend\Crypt\PublicKey\Rsa::encrypt()` and `decrypt()` (though typically this
should only apply to the latter):

```php
$decrypted = $rsa->decrypt($data, $key, $mode, OPENSSL_PKCS1_PADDING);
```

where `$rsa` is an instance of `Zend\Crypt\PublicKey\Rsa`.

(The `$key` and `$mode` argument defaults are `null` and
`Zend\Crypt\PublicKey\Rsa::MODE_AUTO`, if you were not using them previously.)

We recommend re-encrypting any such values using the new defaults.

## 2.4.9 - 2015-11-23

### Added

- Nothing.

### Deprecated

- Nothing.

### Removed

- Nothing.

### Fixed

- **ZF2015-10**: `Zend\Crypt\PublicKey\Rsa\PublicKey` has a call to `openssl_public_encrypt()`
which used PHP's default `$padding` argument, which specifies
`OPENSSL_PKCS1_PADDING`, indicating usage of PKCS1v1.5 padding. This padding
has a known vulnerability, the
[Bleichenbacher's chosen-ciphertext attack](http://crypto.stackexchange.com/questions/12688/can-you-explain-bleichenbachers-cca-attack-on-pkcs1-v1-5),
which can be used to recover an RSA private key. This release contains a patch
that changes the padding argument to use `OPENSSL_PKCS1_OAEP_PADDING`.

Users upgrading to this version may have issues decrypting previously stored
values, due to the change in padding. If this occurs, you can pass the
constant `OPENSSL_PKCS1_PADDING` to a new `$padding` argument in
`Zend\Crypt\PublicKey\Rsa::encrypt()` and `decrypt()` (though typically this
should only apply to the latter):

```php
$decrypted = $rsa->decrypt($data, $key, $mode, OPENSSL_PKCS1_PADDING);
```

where `$rsa` is an instance of `Zend\Crypt\PublicKey\Rsa`.

(The `$key` and `$mode` argument defaults are `null` and
`Zend\Crypt\PublicKey\Rsa::MODE_AUTO`, if you were not using them previously.)

We recommend re-encrypting any such values using the new defaults.
17 changes: 13 additions & 4 deletions src/PublicKey/Rsa.php
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ public function verify(
* @return string
* @throws Rsa\Exception\InvalidArgumentException
*/
public function encrypt($data, Rsa\AbstractKey $key = null)
public function encrypt($data, Rsa\AbstractKey $key = null, $padding = null)
{
if (null === $key) {
$key = $this->options->getPublicKey();
Expand All @@ -259,7 +259,11 @@ public function encrypt($data, Rsa\AbstractKey $key = null)
throw new Exception\InvalidArgumentException('No key specified for the decryption');
}

$encrypted = $key->encrypt($data);
if (null === $padding) {
$encrypted = $key->encrypt($data);
} else {
$encrypted = $key->encrypt($data, $padding);
}

if ($this->options->getBinaryOutput()) {
return $encrypted;
Expand Down Expand Up @@ -288,7 +292,8 @@ public function encrypt($data, Rsa\AbstractKey $key = null)
public function decrypt(
$data,
Rsa\AbstractKey $key = null,
$mode = self::MODE_AUTO
$mode = self::MODE_AUTO,
$padding = null
) {
if (null === $key) {
$key = $this->options->getPrivateKey();
Expand All @@ -314,7 +319,11 @@ public function decrypt(
break;
}

return $key->decrypt($data);
if (null === $padding) {
return $key->decrypt($data);
} else {
return $key->decrypt($data, $padding);
}
}

/**
Expand Down
15 changes: 11 additions & 4 deletions src/PublicKey/Rsa/PrivateKey.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,18 +79,19 @@ public function getPublicKey()
* Encrypt using this key
*
* @param string $data
* @param integer $padding
* @return string
* @throws Exception\RuntimeException
* @throws Exception\InvalidArgumentException
*/
public function encrypt($data)
public function encrypt($data, $padding = OPENSSL_PKCS1_PADDING)
{
if (empty($data)) {
throw new Exception\InvalidArgumentException('The data to encrypt cannot be empty');
}

$encrypted = '';
$result = openssl_private_encrypt($data, $encrypted, $this->getOpensslKeyResource());
$result = openssl_private_encrypt($data, $encrypted, $this->getOpensslKeyResource(), $padding);
if (false === $result) {
throw new Exception\RuntimeException(
'Can not encrypt; openssl ' . openssl_error_string()
Expand All @@ -103,12 +104,18 @@ public function encrypt($data)
/**
* Decrypt using this key
*
* Starting in 2.4.9/2.5.2, we changed the default padding to
* OPENSSL_PKCS1_OAEP_PADDING to prevent Bleichenbacher's chosen-ciphertext
* attack.
*
* @see http://archiv.infsec.ethz.ch/education/fs08/secsem/bleichenbacher98.pdf
* @param string $data
* @param integer $padding
* @return string
* @throws Exception\RuntimeException
* @throws Exception\InvalidArgumentException
*/
public function decrypt($data)
public function decrypt($data, $padding = OPENSSL_PKCS1_OAEP_PADDING)
{
if (!is_string($data)) {
throw new Exception\InvalidArgumentException('The data to decrypt must be a string');
Expand All @@ -118,7 +125,7 @@ public function decrypt($data)
}

$decrypted = '';
$result = openssl_private_decrypt($data, $decrypted, $this->getOpensslKeyResource());
$result = openssl_private_decrypt($data, $decrypted, $this->getOpensslKeyResource(), $padding);
if (false === $result) {
throw new Exception\RuntimeException(
'Can not decrypt; openssl ' . openssl_error_string()
Expand Down
15 changes: 11 additions & 4 deletions src/PublicKey/Rsa/PublicKey.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,19 +68,25 @@ public function __construct($pemStringOrCertificate)
/**
* Encrypt using this key
*
* Starting in 2.4.9/2.5.2, we changed the default padding to
* OPENSSL_PKCS1_OAEP_PADDING to prevent Bleichenbacher's chosen-ciphertext
* attack.
*
* @see http://archiv.infsec.ethz.ch/education/fs08/secsem/bleichenbacher98.pdf
* @param string $data
* @param string $padding
* @throws Exception\InvalidArgumentException
* @throws Exception\RuntimeException
* @return string
*/
public function encrypt($data)
public function encrypt($data, $padding = OPENSSL_PKCS1_OAEP_PADDING)
{
if (empty($data)) {
throw new Exception\InvalidArgumentException('The data to encrypt cannot be empty');
}

$encrypted = '';
$result = openssl_public_encrypt($data, $encrypted, $this->getOpensslKeyResource());
$result = openssl_public_encrypt($data, $encrypted, $this->getOpensslKeyResource(), $padding);
if (false === $result) {
throw new Exception\RuntimeException(
'Can not encrypt; openssl ' . openssl_error_string()
Expand All @@ -94,11 +100,12 @@ public function encrypt($data)
* Decrypt using this key
*
* @param string $data
* @param string $padding
* @throws Exception\InvalidArgumentException
* @throws Exception\RuntimeException
* @return string
*/
public function decrypt($data)
public function decrypt($data, $padding = OPENSSL_PKCS1_PADDING)
{
if (!is_string($data)) {
throw new Exception\InvalidArgumentException('The data to decrypt must be a string');
Expand All @@ -108,7 +115,7 @@ public function decrypt($data)
}

$decrypted = '';
$result = openssl_public_decrypt($data, $decrypted, $this->getOpensslKeyResource());
$result = openssl_public_decrypt($data, $decrypted, $this->getOpensslKeyResource(), $padding);
if (false === $result) {
throw new Exception\RuntimeException(
'Can not decrypt; openssl ' . openssl_error_string()
Expand Down
12 changes: 7 additions & 5 deletions test/PublicKey/RsaTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,15 @@ public function setUp()

$this->userOpenSslConf = realpath(__DIR__ . '/../_files/openssl.cnf');

$this->privateKey = new Rsa\PrivateKey($this->testPemString);

$rsaOptions = new RsaOptions([
'private_key' => new Rsa\PrivateKey($this->testPemString),
'private_key' => $this->privateKey
]);
$this->rsa = new Rsa($rsaOptions);

$rsaOptions = new RsaOptions([
'private_key' => new Rsa\PrivateKey($this->testPemString),
'private_key' => $this->privateKey,
'binary_output' => false
]);
$this->rsaBase64Out = new Rsa($rsaOptions);
Expand Down Expand Up @@ -413,7 +415,7 @@ public function testRsaLoadsPassphrasedKeys()
public function testZf3492Base64DetectDecrypt()
{
$data = 'vNKINbWV6qUKGsmawN8ii0mak7PPNoVQPC7fwXJOgMNfCgdT+9W4PUte4fic6U4A6fMra4gv7NCTESxap2qpBQ==';
$this->assertEquals('1234567890', $this->rsa->decrypt($data));
$this->assertEquals('1234567890', $this->rsa->decrypt($data, null, Rsa::MODE_AUTO, OPENSSL_PKCS1_PADDING));
}

public function testZf3492Base64DetectVerify()
Expand All @@ -425,7 +427,7 @@ public function testZf3492Base64DetectVerify()
public function testDecryptBase64()
{
$data = 'vNKINbWV6qUKGsmawN8ii0mak7PPNoVQPC7fwXJOgMNfCgdT+9W4PUte4fic6U4A6fMra4gv7NCTESxap2qpBQ==';
$this->assertEquals('1234567890', $this->rsa->decrypt($data, null, Rsa::MODE_BASE64));
$this->assertEquals('1234567890', $this->rsa->decrypt($data, null, Rsa::MODE_BASE64, OPENSSL_PKCS1_PADDING));
}

public function testDecryptCorruptBase64()
Expand All @@ -438,7 +440,7 @@ public function testDecryptCorruptBase64()
public function testDecryptRaw()
{
$data = 'vNKINbWV6qUKGsmawN8ii0mak7PPNoVQPC7fwXJOgMNfCgdT+9W4PUte4fic6U4A6fMra4gv7NCTESxap2qpBQ==';
$this->assertEquals('1234567890', $this->rsa->decrypt(base64_decode($data), null, Rsa::MODE_RAW));
$this->assertEquals('1234567890', $this->rsa->decrypt(base64_decode($data), null, Rsa::MODE_RAW, OPENSSL_PKCS1_PADDING));
}

public function testDecryptCorruptRaw()
Expand Down

0 comments on commit b7a5f99

Please sign in to comment.