@ 2016-03-16T14:08:42Z <?php
class QR
{
const METHOD = "aes-256-ctr";
/**
*
* Encrypts (without authenticating)
*
* @param string $input - json encoded array
* @param string $key - encryption key (raw binary expected)
* @param boolean $encode - set to TRUE to return base64-encoded
* @return string (raw binary)
*/
public static function encrypt($input, $key, $encode = false)
{
$nonceSize = openssl_cipher_iv_length(self::METHOD);
$nonce = openssl_random_pseudo_bytes($nonceSize);
$ciphertext = openssl_encrypt(
$input,
self::METHOD,
$key,
OPENSSL_RAW_DATA,
$nonce
);
//Pack the IV and ciphertext together - concatenate
return ($encode ? base64_encode($nonce.$ciphertext) : $nonce.$ciphertext);
}
/**
*
* Decrypts (but does not verify)
*
* @param string $input - ciphertext message
* @param string $key - encryption key
* @param boolean $encoded - are we expecting an encoded string?
* @return string
*/
public static function decrypt($input, $key, $encoded = false)
{
if($encoded){
$input = base64_decode($message, true);
if($input===false){
throw new Exception("Encryption failure");
}
}
$nonceSize = openssl_cipher_iv_length(self::METHOD);
$nonce = mb_substr($input, 0, $nonceSize, "8bit");
$ciphertext = mb_substr($input, $nonceSize, null, "8bit");
$plaintext = openssl_decrypt(
$ciphertext,
self::METHOD,
$key,
OPENSSL_RAW_DATA,
$nonce
);
return $plaintext;
}
}
class QRCrypt extends QR
{
const HASH_ALGO = "sha256";
/**
* Encrypts then MACs an input
*
* @param string $input - plaintext input
* @param string $key - encryption key (raw binary expected)
* @param boolean $encode - set to TRUE to return a base64-encoded string
* @return string (raw binary)
*/
public static function encrypt($input, $key, $encode = false)
{
list($encKey, $authKey) = self::splitKeys($key);
//Pass to QR::encrypt
$ciphertext = parent::encrypt($input, $encKey);
//Calculate a MAC of hte IV and ciphertext
$mac = hash_hmac(self::HASH_ALGO, $ciphertext, $authKey, true);
return ($encode ? base64_encode($mac.$ciphertext) : $mac.$ciphertext);
}
/**
* Decrypts an input (after verifying integrity)
*
* @param string $input - ciphertext input
* @param string $key - encryption key (raw binary expected)
* @param boolean $encoded - are we expecting an encoded string?
* @return string (raw binary)
*/
public static function decrypt($input, $key, $encoded = false)
{
list($encKey, $authKey) = self::splitKeys($key);
if($encoded){
$input = base64_decode($input, true);
if($input===false){
throw new Exception("Encryption failure");
}
}
//Hash size in case HASH_ALGO is changed
$hs = mb_strlen(hash(self::HASH_ALGO, "", true), "8bit");
$mac = mb_substr($message, 0, $hs, "8bit");
$ciphertext = mb_substr($message, $hs, null, "8bit");
$calculated = hash_hmac(
self::HASH_ALGO,
$ciphertext,
$authKey,
true
);
if(!self::hashEquals($mac, $calculated)){
throw new Exception("Encryption failure");
}
//Pass to QR::decrypt
$plaintext = parent::decrypt($ciphertext, $encKey);
return $plaintext;
}
/**
* Splits a key into two separate keys; one for encryption
* and the other for authenticaiton
*
* @param string $masterKey (raw binary)
* @return array (two raw binary strings)
*/
protected static function splitKeys($masterKey)
{
// You really want to implement HKDF here instead!
return [
hash_hmac(self::HASH_ALGO, 'ENCRYPTION', $masterKey, true),
hash_hmac(self::HASH_ALGO, 'AUTHENTICATION', $masterKey, true)
];
}
/**
* Compare two strings without leaking timing information
*
* @param string $a
* @param string $b
* @ref https://paragonie.com/b/WS1DLx6BnpsdaVQW
* @return boolean
*/
protected static function hashEquals($a, $b)
{
if (function_exists('hash_equals')) {
return hash_equals($a, $b);
}
$nonce = openssl_random_pseudo_bytes(32);
return hash_hmac(self::HASH_ALGO, $a, $nonce) === hash_hmac(self::HASH_ALGO, $b, $nonce);
}
}
/**
* QR Code encryption/decryption
*
*/
//Set key here
$key = '5B40ACCA3F81EFCFEA49139E7D7B6944ED716B76726F88DE2ECFAD30457ADD01';
//Set directory of qr.classes.php here with trailing slash
$class = "./";
//Set input here
$input = '{"id":2259,"name":"Darryl Rose"}';
//Are we encrypting or decrypting? (Set to "decrypt" for decryption)
$decrypt = "encrypt";
/********************************************************************************/
$key = hex2bin($key);
$input = trim(htmlspecialchars_decode($input));
$decrypt = $decrypt=="decrypt" ? true : false;
exit(($decrypt ? QRCrypt::decrypt($input,$key,true) : QRCrypt::encrypt($input,$key,true)));
Enable javascript to submit You have javascript disabled. You will not be able to edit any code.
Output for 7.2.29 - 7.2.33 , 7.3.16 - 7.3.33 , 7.4.3 - 7.4.33 , 8.0.0 - 8.0.30 , 8.1.0 - 8.1.27 , 8.2.0 - 8.2.17 , 8.3.0 - 8.3.4 Fatal error: Uncaught Error: Call to undefined function openssl_cipher_iv_length() in /in/3VLT0:19
Stack trace:
#0 /in/3VLT0(86): QR::encrypt('{"id":2259,"nam...', 'I\x89\x03\xDE\xA3\xDD\xBCGsrs\x02\xE5\x8C\x10...')
#1 /in/3VLT0(191): QRCrypt::encrypt('{"id":2259,"nam...', '[@\xAC\xCA?\x81\xEF\xCF\xEAI\x13\x9E}{i...', true)
#2 {main}
thrown in /in/3VLT0 on line 19
Process exited with code 255 . Output for 7.1.7 XhO/BTWKOB17BBILxeOAhuK4tCYoeIuMFIZ2zrTO3oM6aQaApAEdOS6FL1r4X6SwhsyGVM9+HwDEnbiLJYPcsM5xeEfoNsHfIJOgMekRhdo= Output for 7.1.6 WorNzXDKNY+cfq/b8/1rDPXRP0cVn2pak9affxw0ESqGvMVbhXvR5LOSXt7AYeQZ0ccKQ/EsyN116OBY/HCTayfHezaG+hvgwu78HZt+tGs= Output for 7.1.5 F3zth/gXCtUcwqTFCSbXKgOkdT1ag8MnSRP64fLFoDQ/fdLqFxEntr6EkBkM8Fr7rd90dBntoZmfWyFzH1/X1MqukNVUom1t8ejZiccV9v4= Output for 7.1.0 SE4TJ4/4mB1BYTz9XwSNIrInxskonZhAop6Wz71vbLrOoQoGPmnbS+EjvoUR9C6nE0Ms7JhTnrAzj6ta+E7awTZKqvJXKAHC1uMBbdJ978Y= Output for 7.0.20 RkQqPaaN8EviP3SS2knGdi4N6YSfbzAvxSSvpXjgMfSYPm5BUj6A4IpNCKFTXS1+Ut11EGGdB2ZWdw8rik47t3VaiJMvvAKBRA0FFkB9fLU= Output for 7.0.6 d0YYFRdT0CS7LODwrTOu4gWF9ARo/KBg5K03Edp34mZxVgTbW/hT60lZfDgjoiM1rPgnntbiqIWCInyOpfAUdRtPhzUAFbLEBtS7it6eQyc= Output for 7.0.5 XPsWViNPStNmwmZncbPVLvFFWYtFFlGwVbBP4xAKrJIPvCHH9LGGVrN655QVIQDegv0+ZjAnmsKktuzYM7+VXy7S8iKG5CtB6NMmustFUD8= Output for 7.0.4 E2dKOJsQ6J+hoeDXwJRBDOZ0r9jhE1vC4yKikuAvJ1hpUjlyHyQumPVZ6k0sM1BiKjxbUgB2MUZ3JLyO7yY+YXhaKMnXrVg9Dyb8iCW9vdI= Output for 7.0.3 zzNPk7gZDAtm7AY/UNO9Rud3W5VclrNqJ3oxVPiJKflG3qgPF3s+Uh2H1mbn8mnkXvzmRIDehYcf9vAVGWPkOPOpbogWOzqdASyfrZdiEVw= Output for 7.0.2 rZcjo5w7BJmrTyj5FZxn8LzX153LhwH9tqeY62CXwLd2fgageFAzP1/BLAoCg/VfyB1cLLsPUfTKsCvmgXZBT7bDE+4nTMpWw1iYabHlYm8= Output for 7.0.1 KRX7zRmDNEsvkTe3VmGCTU29gyJwEcXhwUFaUEvwD2SYPTYKlIM2/Uqh0O90X6zqzxY2bRxnf5FBzDhNrjihdC1AjmhFjRDjwosY43V9Gmc= Output for 7.0.0 XeHUlCGK8yVEIjciMUCGetFd8CNNVN8fUUS8QEGPDMOBEktnXACwWAohNkN5o1xL2pUXRBEiVKjUzSc/BJN6lmwg3U4tvg6dmuWBwQk6C1E= Output for 5.6.28 nlqWH2IayWZ3D1uOs6WqRGoKsTXfSbf120FhI7bA/5II5HGfjrdRG8/7rWuMGYGlxS8YMrjYkI6obVMqf+1Uvvp9AxQXFFHLg9wPMy3pII8= Output for 5.6.21 J8b88svjZg36J2V1rganzhne0lHiW8A1Eg2HWMu411i03PjpmdAUkq18kHk4g29i2ytJNdl/BOwCt67zZlr2L2gFLFRpPt+A2j7/ZQkr6jI= Output for 5.6.20 SMSTeiCD/ShO5jbyVVXOsf1/RO0fbuIHCq8gSpAy6wVWIJ6ojfuFslX2aWc+yTRp7oqXtS0vTxKU++xKnwJoR8dpnlyWc6vXSMqVmEKuOWw= Output for 5.6.19 ttVqxH3y3hYBtoIvsh5+QUBnYTXQp2Rdwjtuk/QuxZadGURlh/mAtiHFbTsgMTXKRe+k8R2jCiGIPYXw1DM7uhkOHsUi/hs+/z3I92WqmWE= Output for 5.6.18 WOnk+7ZNAZGaCA3vjbPGc0t7qQThmRzqYiP/QJWYVA6yxCLs7bb/RWHFSfjb8Hkkc51pDw7tH4yR/eJTJxs+jfMne7TGAjxtzQJl3zGo8Fs= Output for 5.6.17 3rfOrD3mnQobsOzVKHG5uKDcOeO3j5lcKr6WDiOcevMKoVkyQUeBwgHc+9I6GyQnAKFKPA0WCcqKYfflAReywBSssVhGxGRldLMockuPhdQ= Output for 5.6.16 u5sWcIZUIyYjR5brVZik44zyhx8nqbEYyjgHJmeblUZyvqafVTVAMaLK4Nv9Ya8RKlrWlVfa2lWzdYxgQN0fSV5mG6Ge1ymngoY44mI6ne8= Output for 5.6.15 Lr/IFnsm1/thb7dx1WuBbUlFe4Z6nG+h4QWRnVZldgas+Wz0p/VdkxLLRlqAfY4+dZUQ+VHLAj660LzUixakRGpiuXIgytqLc3LFPRqmK6k= Output for 5.6.14 ZOJmYIHory6IVbJYzHzSWPaMVwwFoStVM7TtigHsRxrrBql4D6pgjUrgK848ZVjHHPTwhBW9VIaIzZM/NQCnCNCbRvXvPRGibH7sW3N9rLI= Output for 5.6.13 cQHpPL8+HyWsXpBowe4zO+rmZTo/RZAd1R0Bx93yprSQgibihMX1+5L9vJr+UrG7yf8MLC/7Jgbkit4zslMU5qgUrGLRdKGjh49j+vKaGtY= Output for 5.6.12 gk77jIi5+oshqOIcHhgvxT/4CAgPKOjJpZ69QkplCRhrf99eTkC8TZd0KQ/rIJ8HYeVNlDIUnlHoJlXLc5mUXLKxTI4Txi5hfHbU0onp+ak= Output for 5.6.11 lfI4MmwkLddgmVjrIhRq3NRn7ZlKXT+aDJL6Fh8tMtNQviwykiWGZfn3t16Cxk+qPYAnyclufg+r9i+rng/pEpUvLjrJ7venWmTQmSvRQtM= Output for 5.6.10 67da21XYKFe9SfslOvs+YTR5X+2S0XbkLqA2MTNb143BbCFngCXFp/AFGU6mAH4po3tOxh7mzdNO9+9ll56wAYUonV0LaXZAV+R+4Alo37M= Output for 5.6.9 YRLjhWbO+Lkt0pLOOp010r7yWrjevsN2vAPPsDWMDxx8U0XJ1pZ47j6lwMAuUqTzQkRrMP34vxVAFQQvvqERwXTmU2TINAu1fXk+4c4T8VY= Output for 5.6.8 Xbzjr8+SDRD2y/KrbuuG/zVr49KHYGZhf9/5a702cAE+biSIUUlXr5y2Zqt/LO9JL+6Ow3Y643dfDvvX4zXaFII0nMF/ey2rmsyCQ6mTjuk= Output for 5.6.7 B3oKHRPhwERM4yEtdEUQiKIG4C2pwBUYy6uTzaUzVZJbawMevITZmULN4Xlq+j6sK58yKmUvmL4rqAMMdncPeaouFcmU/k/XWTWD+ojBLkg= Output for 5.6.6 yZjzY9WZfg5V+xX51W/kJPeyh0wIBk6xxxJQWPyIw/LmqDt+XSevqJ/P1TWkz6fIgpugWGAOIWoJM/XLxN5nNP8tVQAXEjQdJuJKtcXNRko= Output for 5.6.5 d8etyIBADbsWnlHW8iFMej9SmaBUjrWdCoEKXz3G2f2hQpt/e9/mr7VgUYUP2gNhjaOnYO09AGw9YI4/cmw3UFqvrLDMuLAROfTO8cVBzlU= Output for 5.6.4 ED/BcLr9UHfmKJiR59BW6czGF3DEcG4u8TuAJFrNOW1H52P5YmbKDM2pJXyXZkYW2WnrhWGY2qElRHcDIWvM86lK9pq+BCrxi4RY7xZSA6E= Output for 5.6.3 /RiB5rqYtIBS66ER8dSKhnSZaixFfUej1dVfm5RmtM1GqFjQY0iBitRcqyIulu1eLcjHWZR7OpgBOQD/mL38tSCQs4VjTg1vLLbEJybzN7Q= Output for 5.6.2 8ZAobimnV7pEIpT98al5zA5aumTQNI2i67dfxzjzKhplgcMfrzd2AHceBUq1VSg0+z+l0mrnkEZfWhk1iMsiRYv6I55GFdrksWvllXZVYWY= Output for 5.6.1 1k4m2cXzK7qzWZqy7eG9TzTOLkgPpMBDlcJ/w4Yn2gxFFZtDlCY/IsU//e73dzn2GOtPSrlDHv6kwXjlYw70pd3cBmFGmVg5mLEe8h0WQ0c= Output for 5.6.0 F4eBtMk+n8RqlAX5HNYOCRV+ZHz3TGgKEg71y+iMIw4jO70WHzoY73ZyGq0npfRQPPOZIaXFbtcBvv7cCho+XhfG37DvxR26eITwYCTnS8g= Output for 5.5.35 SFOu8RIbylzmoUkSIKKXKveNO1E8/sF3YMdhXA4a6JSsIV5PzyO50b9oN3AiMvJsWgg0gNVTmWFhDzMRVp72qxrHaw4m1RjPO1eTxopGeNU= Output for 5.5.34 bJiclGHem5sjIVYlIrMmbYIqticcz/BbemlUgKdM+6p+Bz/FsK2o2I/K+mcCqKokFT7NI2Xs9VTEWVP87Snx17SPUbvgc5/Oi5j/vK2y0gI= Output for 5.5.33 ieZL5LQ0Fagp9AJzaLQ2un60PwnZZOrW/IHQVrMyRMdI5Dzn2+AYqejRjDDYVXiPPEuh+mAVzbQXa5x5eP8v8tsASgzxPzYip0ygKRwKY9U= Output for 5.5.32 dlEyHG/iLW2f3pCWkmkeqsRnOh7tgKfCbeDsD5WvLZbY/n6I5B0eARg54Y28zB/YNhuMQmDLGv25SS76vPOimOI6gDMkcAh9B5t/+AhhPGk= Output for 5.5.31 SPX25+3gApjQgv9OaYViMLt7nwEuR1nkAxj879DLubVXr3hi0Y74204U2qAV53jN2mfWKWZBQ/9vceDAT7PkSY+PG2AGAC8SfLBun965/6E= Output for 5.5.30 8WXvl1VF+xB6Mp8Pi/mN3Ux96u7x+sbbBeJ56o4KDqv7IRp966rg+c4Epz39jx8vw4IvH08APcqn5qtorvalMFED4el/bl9p5vhhpdE/5N8= Output for 5.5.29 4IMZ+ERk9S/XK2PJmgkyB8ZibCGv3+rVlBC+USf6pf1S/FwnyxSl9w+WFJtWMoqHAKbQKiR0ZuAM0g2QuzGSyYzYhAWBfljbY1/w0xVtAxw= Output for 5.5.28 ivHAv8D3IF592PNQTPDrIw4PkVer98UF0+RwfRLflKg9V58LTDcZlELObKMCbTI8UPjuiErdz7b4gIWalLNkEJLhq/AOJ/lRNuFa0ZPfkrU= Output for 5.5.27 fgY03xovkWmToiaq9QCTG+zcDg3EUrfiEpPl5p0i87n632dVEpPs1blO8/d+/coClBotZ9T2F8hIIrvXRZMM8HYWLjlriMPPXXKgWGVcGRs= Output for 5.5.26 kmrv+E9NxKCAht7XHvrNKktzsKL7XpOnWBq1oelG8KGhbDpzJPbqZy4KXJFWlXZQUjhQToswAkIZxAzZsfHdLjr7gd9AssvXRiqvUWyNjfI= Output for 5.5.25 JgFcyOYslMWA/KG32XKSrXW5fBAzz1PWgd1oBbvT+fJGQdmyPx5PBOR1OFHJPoTx7MRccUM3Bmi/Om+bspJqjXasrnp1PcGPY8ucqKzl7Dg= Output for 5.5.24 P+VQ8HRw5q6iF//YWYIa0aK2WzVst5O7TVoB8DP6DXxK1EoB46lns1pTL4OW0DvxZ/zFTBXuubgKzOs+skaM/V5533OkIYpP98XOKUjh+Kk= Output for 5.5.23 awY1p5gSmIjxb8YH1uqdhBm+QDJmQAZ+S1vK0WJoEw8o3v9uYo0xaRBtT/c8680H15LPDh3ApRVJ41ZmKliRDI1OEhRAVbYkpkIttPONRzI= Output for 5.5.22 Xnbz0JFQRPcZeuPEZdOlWiOQgscNcp16XX6GbyHOCDI3VqlzUGYP4aZVeOekCbXf4y/njttMhMYSCf5GDg60I2JnI6mIkChRg30hHHTQiF8= Output for 5.5.21 HNlrBFB5cs+QGghbk8YZp0x1/itghUmiFXUDGCIjmVmIEYirBC2cC0EyNVwcKG1C1DDsE/88SXr4/GE4xFAo3w7Q/0peVBcS0qgDWvnhLWU= Output for 5.5.20 CjuuT9MDEMaWmn/3Fo3xfYErUDBBv13vKkZn7An4xzn/j56p6sOmNbdILc/RW7YZk/RuDDrfIb50AfKva2Fj2CU0DDpLSScP2XMtckhpSRA= Output for 5.5.19 h5qFGgsAZJFte1AVzUPOGKHbPiyO3Sb2qJ2dj7dj1a5P98z1CpmcDRr09NKy0eYam93Ook5CCv9MYrWZb3CexCFU3i79uvTrAeJwBEfL4Hg= Output for 5.5.18 BIsmsNvCOjE2Kc35/0NVTPEe3hOzxlmabmvH4WI5Yok/iwco1dlvRvVAdVUgLUoydtKvDoK23flexABDPElzTnT4ga8FP4hLUQAwGXdgI7g= Output for 5.5.16 1NrCZXI42NNQi3+d6maDF/x2+vVumgwkXL+kuLKcu3MFdAbM8nzHReU961+3mUnbC2vqeb8OgTahVY3Dl66qTvbOoZTUqH8ZmzRRLp3ael0= Output for 5.5.15 09z0uUEZCAKKTuVCKx2N+F2aSablzVwITpjXCOdE3fbHXNdwr0HIfapwcZocgGx4zCw8oR79EShTA9hizWxLdLgZn1tFXF8XcKa9BeJvq0M= Output for 5.5.14 SZiZNDinbiTEOa4DGAbhrpaMszRFX/Db7iqx8W5gASOGdeRjbGVpoWxQD7mS5GHgok01LRNQo7ZvLG3VxZ4zqdy/Be0QJgPccytRu/Z5iFg= Output for 5.5.13 0SRVn5Wnl02twiEua8QZ5xccmJvE8d15MQ4t9bTtsbHbE2wJiSh47CCXOFaOFh05Vu606BDcpWhjwGSfTzh6V0fnYtz6Jvu7RB+cwZ04Mq0= Output for 5.5.12 BYJK06BpFMFYj17sz9lhLKZ7IE1A68Pw1RTB7zaA5EbPTcRWztEWVJGM9ZdvXru4VJNlVVL79fohy6cqr1ZhNMmCbNa0hoLjKuExKj8xlI0= Output for 5.5.11 yu9OPwsb4UO2OEE0x/kbq85h8GgBrCj+R3OIDBy0FvxXhotXPRmHa5FwGf+y5AUJQfAWK3ylwbxlbYzjmc6qDjQcphug9mvPILNmtrUkrjY= Output for 5.5.10 8SzFoDbhRDvBOqGjj35bNQIT0kOKVkC2u1WMokhAbkpBFp4WPczMG1wb73tLPiJUfi/432us1e90kiS81yjfAw9TPGz1SlJhXTGEvExkO4g= Output for 5.5.9 ubEwnUnohP+XkQQ94iPrIgz95YBZD4Bb52dP45+9Q63qZJ3otMUcCVFAF4QyHZ7pwAhAKagB2tRiA/H2z9rROFbdDjfqUhYLtkS/KLaeHp8= Output for 5.5.8 GDY7GZeyPZ4skMkkb8re0bRDnSD0LoK6e5dV/8pUq+V2/R/VOIU0jeYx/NO6T+Qt+Fqu4N5OW+LUaRoTmaNozfMkCgDXsehZpOjisyq1M7A= Output for 5.5.7 FI5teG4jfEqI1tHlKP3H+YBUC0VbEwrA6QECDA6fyXfcNRcNVtdpeRPcclxr33rQhpL78E3ZdkdxyW/XYwofSW9+3SDwho372A5nUH3PehQ= Output for 5.5.6 Zq5eJOsIYQAMVuI7u/A8jXatqIX+B23B8DQzoOjRRwaejRobLUfse+rATi4d6Tnqi9D9MtZGDxUPPkz1nHPRlD72i5lKtf1Odxgk+Z/hIuA= Output for 5.5.5 Lb7N2RYQlelKEKh9OLSkycqUoNgecokXKhFn9meFmwjzfGKTlPBu8xsDu2O0Q6m268uqhiIYULFaVCXFlglnChI0iL0mxIF6Hc/x0b6QDyM= Output for 5.5.4 gFGWnRwtHQ2EWxNiy0cGCz1s3tWFOF6pyVEz7jKt6MPcF36IXoQfA37LvIiirUzKbgVwUcA8fJiG0cbg0vBtd/BuiXiflav95Fuprc1FYI8= Output for 5.5.3 iGOMXKLFj8xc9IFJVrII3DET+8GLsMkUn54+OdP+kKgwlCBH6PkfcMAGWJu4hLzRvfGDFAE/KuMK0OwAAf9+iBUYMRnqQz4OZ4XrrO+ukMA= Output for 5.5.2 QWpk5c9xXas1VLKZVYFwXOnvXpwqTG17toogWujglv78SbwL1UYj1RCM0sN0NDJ+2OyCYhLyb9CvybuNmvlb3JAXDo8GG5W7C91yqPPz6Sk= Output for 5.5.1 9bjnuxi3X9Afm/77ZAPd2FsjkxJ4ODoxOu8LIubO2x6xDvXKQ5LzcWPHjua5/VAZsFsNJ6KXAophQkGI5idGk0DL/QxehuHqSALnkmY7eEU= Output for 5.5.0 tDl5/9iJI9YADZbj+YJB/2pJJxHtNFFJn6t84Pxqea/C6qB4RNw7JErY5BZG86KSkBWwKeHF5SMIQ2uAbm386Tds+H5MVYFigD5bOzrEEzw= preferences:dark mode live preview
145.22 ms | 401 KiB | 205 Q