forked from thenativeweb/eventsourcingdb-client-php
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSigningKey.php
More file actions
107 lines (82 loc) · 3 KB
/
SigningKey.php
File metadata and controls
107 lines (82 loc) · 3 KB
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
99
100
101
102
103
104
105
106
107
<?php
declare(strict_types=1);
namespace Thenativeweb\Eventsourcingdb;
use RuntimeException;
define('LESS_THAN_PHP_VERSION_84', PHP_VERSION_ID < 80400);
if (LESS_THAN_PHP_VERSION_84) {
define('OPENSSL_KEYTYPE_ED25519', 5);
}
final class SigningKey
{
public readonly string $privateKeyPem;
public readonly string $publicKeyPem;
public readonly Ed25519 $ed25519;
public function __construct()
{
if (LESS_THAN_PHP_VERSION_84) {
$keypair = sodium_crypto_sign_keypair();
$secretKey = sodium_crypto_sign_secretkey($keypair);
$privateKey = substr($secretKey, 0, 32);
$publicKey = sodium_crypto_sign_publickey($keypair);
$this->privateKeyPem = $this->generatePem($privateKey, 'PRIVATE KEY');
$this->publicKeyPem = $this->generatePem($publicKey, 'PUBLIC KEY');
$this->ed25519 = new Ed25519(
privateKey: $privateKey,
publicKey: $publicKey,
);
return;
}
$privateKeyPem = '';
$privateKeyRes = openssl_pkey_new([
'private_key_type' => OPENSSL_KEYTYPE_ED25519,
]);
if ($privateKeyRes === false) {
throw new RuntimeException('Failed to generate Ed25519 key pair.');
}
openssl_pkey_export($privateKeyRes, $privateKeyPem, null, [
'private_key_type' => OPENSSL_KEYTYPE_ED25519,
]);
$details = openssl_pkey_get_details($privateKeyRes);
$this->privateKeyPem = $privateKeyPem;
$this->publicKeyPem = $details['key'] ?? '';
$this->ed25519 = new Ed25519(
privateKey: $details['ed25519']['priv_key'] ?? '',
publicKey: $details['ed25519']['pub_key'] ?? '',
);
}
private function generatePem(string $key, string $type): string
{
$encodePkcs8 = $this->encodePkcs8($key);
$pem = "-----BEGIN {$type}-----\n";
$pem .= chunk_split(base64_encode($encodePkcs8), 64, "\n");
$pem .= "-----END {$type}-----\n";
return $pem;
}
private function encodeLength(int $length): string
{
if ($length < 0x80) {
return $this->chr($length);
}
$lenBytes = ltrim(pack('N', $length), "\x00");
return $this->chr(0x80 | strlen($lenBytes)) . $lenBytes;
}
private function asn1(int $tag, string $value): string
{
return $this->chr($tag) . $this->encodeLength(strlen($value)) . $value;
}
private function chr(int $code): string
{
if ($code < 0 || $code > 255) {
throw new RuntimeException('Code must be between 0 and 255.');
}
return chr($code);
}
private function encodePkcs8(string $key): string
{
$version = $this->asn1(0x02, "\x00");
$algOid = $this->asn1(0x30, $this->asn1(0x06, "\x2B\x65\x70"));
$privateKey = $this->asn1(0x04, $this->asn1(0x04, $key));
$pkcs8 = $this->asn1(0x30, $version . $algOid . $privateKey);
return $pkcs8;
}
}