Skip to content

Commit 33453af

Browse files
committed
Merge remote-tracking branch 'origin/payload' into payload
2 parents 5c88f17 + ee76313 commit 33453af

File tree

4 files changed

+37
-37
lines changed

4 files changed

+37
-37
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,9 @@ $browser = $webPush->getBrowser();
100100
Not until the [Push API spec](http://www.w3.org/TR/push-api/) is finished.
101101

102102
### What about security?
103-
Internally, WebPush uses the [phpecc](https://github.com/phpecc/phpecc) Elliptic Curve Cryptography library.
103+
Internally, WebPush uses the [phpecc](https://github.com/phpecc/phpecc) Elliptic Curve Cryptography library to create
104+
local public and private keys and compute the shared secret.
105+
Then, WebPush uses `openssl` in order to encrypt the payload with the encryption key.
104106

105107
### How to solve "SSL certificate problem: unable to get local issuer certificate" ?
106108
Your installation lacks some certificates.

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
"require": {
1616
"php": ">=5.4",
1717
"kriswallsmith/buzz": ">=0.6",
18-
"mdanter/ecc": "^0.3.0"
18+
"mdanter/ecc": "^0.3.0",
19+
"lib-openssl": "*"
1920
},
2021
"require-dev": {
2122
"phpunit/phpunit": "4.8.*"

src/WebPush.php

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@
1616
use Buzz\Client\MultiCurl;
1717
use Buzz\Exception\RequestException;
1818
use Buzz\Message\Response;
19+
use Mdanter\Ecc\Crypto\Key\PublicKey;
1920
use Mdanter\Ecc\EccFactory;
20-
use Mdanter\Ecc\Message\MessageFactory;
21+
use Mdanter\Ecc\Serializer\Point\UncompressedPointSerializer;
2122

2223
class WebPush
2324
{
@@ -134,40 +135,40 @@ public function sendNotifications(array $endpoints, array $payloads = null, arra
134135
* @param string $payload
135136
*
136137
* @return array
137-
*
138-
* @throws
139138
*/
140139
private function encrypt($userPublicKey, $payload)
141140
{
142-
throw new \ErrorException('Encryption does not work yet.');
143-
144-
// get local curve
145-
$localCurveGenerator = EccFactory::getNistCurves()->generator256();
146-
$localPrivateKey = $localCurveGenerator->createPrivateKey();
147-
$localPublicKey = $localPrivateKey->getPublicKey();
148-
// var sharedSecret = localCurve.computeSecret(userPublicKey);
141+
// initialize utilities
142+
$math = EccFactory::getAdapter();
143+
$keySerializer = new UncompressedPointSerializer($math);
144+
$curveGenerator = EccFactory::getNistCurves()->generator256();
145+
$curve = EccFactory::getNistCurves()->curve256();
149146

150-
//var salt = crypto.randomBytes(16);
147+
// get local key pair
148+
$localPrivateKeyObject = $curveGenerator->createPrivateKey();
149+
$localPublicKeyObject = $localPrivateKeyObject->getPublicKey();
150+
$localPublicKey = base64_encode(hex2bin($keySerializer->serialize($localPublicKeyObject->getPoint())));
151151

152-
//ece.saveKey('webpushKey', sharedSecret);
152+
// get user public key object
153+
$userPublicKeyObject = new PublicKey($math, $curveGenerator, $keySerializer->unserialize($curve, bin2hex(base64_decode($userPublicKey))));
153154

154-
/*var cipherText = ece.encrypt(payload, {
155-
keyid: 'webpushKey',
156-
salt: urlBase64.encode(salt),
157-
});*/
155+
// get shared secret from user public key and local private key
156+
$sharedSecret = $userPublicKeyObject->getPoint()->mul($localPrivateKeyObject->getSecret())->getX();
158157

159-
$messages = new MessageFactory(EccFactory::getAdapter());
160-
$message = $messages->plaintext($payload, 'sha256');
158+
// generate salt
159+
$salt = openssl_random_pseudo_bytes(16);
161160

162-
$dh = $localPrivateKey->createExchange($messages, $userPublicKey);
163-
$salt = hash('sha256', $dh->calculateSharedKey(), true);
161+
// get encryption key
162+
$encryptionKey = hash_hmac('sha256', $salt, $sharedSecret);
164163

165-
$cipherText = $dh->encrypt($message)->getContent();
164+
// encrypt
165+
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-128-gcm'));
166+
$cipherText = openssl_encrypt($payload, 'aes-128-gcm', $encryptionKey, false, $iv); // base 64 encoded
166167

167168
return array(
168-
'localPublicKey' => base64_encode($localPublicKey),
169+
'localPublicKey' => $localPublicKey,
169170
'salt' => base64_encode($salt),
170-
'cipherText' => base64_encode($cipherText),
171+
'cipherText' => $cipherText,
171172
);
172173
}
173174

tests/WebPushTest.php

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -91,17 +91,13 @@ public function testEncrypt()
9191
$encrypt = $class->getMethod('encrypt');
9292
$encrypt->setAccessible(true);
9393

94-
$expected = array(
95-
'localPublicKey' => 'BH_1HZcs53fCIMW7Q6ePJqCqc4JIzSeCTjcNBmoet2eMObvQTpiBHH0EnDYZ0kTqk5f2b6wruq7US1vewtngt6o',
96-
'salt' => '0HK6QfkQmcQKFVAgG2iOTw',
97-
'cipherText' => 'ivmuewrVd-7qkRgxRcu972JyrSvXJzbLeWhTXx1FRZndeP5PVS3fnLQhmK077PgW7C5MLAA_wzDpIN_oB9vo',
98-
);
99-
100-
$actualUserPublicKey = 'BDFsuXPNuJ4SxoYcVVvRagonMcSKHXjsif4qmzpXTDyy29ZKqbwtVAgHCLJGP0HgQ0hpkg6H5-fPBvDjBQxjYfc';
101-
$actualPayload = '{"action":"chatMsg","name":"Bob","msg":"test"}';
102-
103-
$actual = $encrypt->invokeArgs($this->webPush, array($actualUserPublicKey, $actualPayload));
104-
105-
$this->assertEquals($expected, $actual);
94+
$res = $encrypt->invokeArgs($this->webPush, array($this->keys['standard'], 'test'));
95+
96+
// I can't really test encryption since I don't have the user private key.
97+
// I can only test if the function executes.
98+
$this->assertArrayHasKey('cipherText', $res);
99+
$this->assertArrayHasKey('salt', $res);
100+
$this->assertArrayHasKey('localPublicKey', $res);
101+
$this->assertEquals(16, strlen(base64_decode($res['salt']))); // should be 16 bytes
106102
}
107103
}

0 commit comments

Comments
 (0)