-
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Utils.php
98 lines (80 loc) · 3.42 KB
/
Utils.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2020-2021 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace WebPush;
use Assert\Assertion;
use function Safe\pack;
use function Safe\unpack;
abstract class Utils
{
private const PART_SIZE = 32;
private const HASH_SIZE = 256;
public static function privateKeyToPEM(string $privateKey, string $publicKey): string
{
$d = unpack('H*', str_pad($privateKey, self::PART_SIZE, "\0", STR_PAD_LEFT))[1];
$der = pack(
'H*',
'3077' // SEQUENCE, length 87+length($d)=32
.'020101' // INTEGER, 1
.'0420' // OCTET STRING, length($d) = 32
.$d
.'a00a' // TAGGED OBJECT #0, length 10
.'0608' // OID, length 8
.'2a8648ce3d030107' // 1.3.132.0.34 = P-256 Curve
.'a144' // TAGGED OBJECT #1, length 68
.'0342' // BIT STRING, length 66
.'00' // prepend with NUL - pubkey will follow
);
$der .= $publicKey;
$pem = '-----BEGIN EC PRIVATE KEY-----'.PHP_EOL;
$pem .= chunk_split(base64_encode($der), 64, PHP_EOL);
$pem .= '-----END EC PRIVATE KEY-----'.PHP_EOL;
return $pem;
}
public static function publicKeyToPEM(string $publicKey): string
{
$der = pack(
'H*',
'3059' // SEQUENCE, length 89
.'3013' // SEQUENCE, length 19
.'0607' // OID, length 7
.'2a8648ce3d0201' // 1.2.840.10045.2.1 = EC Public Key
.'0608' // OID, length 8
.'2a8648ce3d030107' // 1.2.840.10045.3.1.7 = P-256 Curve
.'0342' // BIT STRING, length 66
.'00' // prepend with NUL - pubkey will follow
);
$der .= $publicKey;
$pem = '-----BEGIN PUBLIC KEY-----'.PHP_EOL;
$pem .= chunk_split(base64_encode($der), 64, PHP_EOL);
$pem .= '-----END PUBLIC KEY-----'.PHP_EOL;
return $pem;
}
public static function computeIKM(string $keyInfo, string $userAgentAuthToken, string $userAgentPublicKey, string $serverPrivateKey, string $serverPublicKey): string
{
$sharedSecret = self::computeAgreementKey($userAgentPublicKey, $serverPrivateKey, $serverPublicKey);
return self::hkdf($userAgentAuthToken, $sharedSecret, $keyInfo, self::PART_SIZE);
}
private static function hkdf(string $salt, string $ikm, string $info, int $length): string
{
// Extract
$prk = hash_hmac('sha256', $ikm, $salt, true);
// Expand
return mb_substr(hash_hmac('sha256', $info."\1", $prk, true), 0, $length, '8bit');
}
private static function computeAgreementKey(string $userAgentPublicKey, string $serverPrivateKey, string $serverPublicKey): string
{
$serverPrivateKeyPEM = self::privateKeyToPEM($serverPrivateKey, $serverPublicKey);
$userAgentPublicKeyPEM = self::publicKeyToPEM($userAgentPublicKey);
$result = openssl_pkey_derive($userAgentPublicKeyPEM, $serverPrivateKeyPEM, self::HASH_SIZE);
Assertion::string($result, 'Unable to compute the agreement key');
return str_pad($result, self::PART_SIZE, "\0", STR_PAD_LEFT);
}
}