Sync your WordPress posts to standard.site records on your PDS
1<?php
2declare(strict_types=1);
3/**
4 * Token encryption at rest using libsodium.
5 *
6 * @package Wireservice
7 */
8
9namespace Wireservice;
10
11if (! defined('ABSPATH')) {
12 exit;
13}
14
15class Encryption
16{
17 /**
18 * Derive an encryption key from WordPress auth constants.
19 *
20 * @return string 32-byte key.
21 */
22 private static function get_key(): string
23 {
24 return sodium_crypto_generichash(
25 AUTH_KEY . AUTH_SALT,
26 "",
27 SODIUM_CRYPTO_SECRETBOX_KEYBYTES,
28 );
29 }
30
31 /**
32 * Encrypt a plaintext string.
33 *
34 * @param string $plaintext The value to encrypt.
35 * @return string Base64-encoded nonce + ciphertext.
36 */
37 public static function encrypt(string $plaintext): string
38 {
39 $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
40 $ciphertext = sodium_crypto_secretbox($plaintext, $nonce, self::get_key());
41
42 return base64_encode($nonce . $ciphertext);
43 }
44
45 /**
46 * Decrypt a previously encrypted string.
47 *
48 * Returns false if decryption fails, which also covers the case
49 * where a plaintext (never-encrypted) value is passed in.
50 *
51 * @param string $encoded Base64-encoded nonce + ciphertext.
52 * @return string|false The plaintext or false on failure.
53 */
54 public static function decrypt(string $encoded): string|false
55 {
56 $decoded = base64_decode($encoded, true);
57
58 if ($decoded === false) {
59 return false;
60 }
61
62 $nonce_length = SODIUM_CRYPTO_SECRETBOX_NONCEBYTES;
63
64 if (strlen($decoded) < $nonce_length + 1) {
65 return false;
66 }
67
68 $nonce = substr($decoded, 0, $nonce_length);
69 $ciphertext = substr($decoded, $nonce_length);
70
71 return sodium_crypto_secretbox_open($ciphertext, $nonce, self::get_key());
72 }
73}