Server : Apache System : Linux webd348.cluster026.gra.hosting.ovh.net 5.15.148-ovh-vps-grsec-zfs-classid #1 SMP Thu Feb 8 09:41:04 UTC 2024 x86_64 User : hednacluml ( 122243) PHP Version : 8.3.9 Disable Function : _dyuweyrj4,_dyuweyrj4r,dl Directory : /home/hednacluml/dieu/ecrire/src/Chiffrer/ |
<?php /***************************************************************************\ * SPIP, Système de publication pour l'internet * * * * Copyright © avec tendresse depuis 2001 * * Arnaud Martin, Antoine Pitrou, Philippe Rivière, Emmanuel Saint-James * * * * Ce programme est un logiciel libre distribué sous licence GNU/GPL. * \***************************************************************************/ namespace Spip\Chiffrer; /** Gestion des clés d’authentification / chiffrement de SPIP */ final class SpipCles { private static array $instances = []; private string $file = _DIR_ETC . 'cles.php'; private Cles $cles; public static function instance(string $file = ''): self { if (empty(self::$instances[$file])) { self::$instances[$file] = new self($file); } return self::$instances[$file]; } /** * Retourne le secret du site (shorthand) * @uses self::getSecretSite() */ public static function secret_du_site(): ?string { return (self::instance())->getSecretSite(); } private function __construct(string $file = '') { if ($file) { $this->file = $file; } $this->cles = new Cles($this->read()); } /** * Renvoyer le secret du site * * Le secret du site doit rester aussi secret que possible, et est eternel * On ne doit pas l'exporter * * Le secret est partagé entre une clé disque et une clé bdd * * @return string */ public function getSecretSite(bool $autoInit = true): ?string { $key = $this->getKey('secret_du_site', $autoInit); $meta = $this->getMetaKey('secret_du_site', $autoInit); // conserve la même longeur. return $key ^ $meta; } /** Renvoyer le secret des authentifications */ public function getSecretAuth(bool $autoInit = false): ?string { return $this->getKey('secret_des_auth', $autoInit); } public function save(): bool { return ecrire_fichier_securise($this->file, $this->cles->toJson()); } /** * Fournir une sauvegarde chiffree des cles (a l'aide d'une autre clé, comme le pass d'un auteur) * * @param string $withKey Clé de chiffrage de la sauvegarde * @return string Contenu de la sauvegarde chiffrée générée */ public function backup( #[\SensitiveParameter] string $withKey ): string { if (count($this->cles)) { return Chiffrement::chiffrer($this->cles->toJson(), $withKey); } return ''; } /** * Restaurer les cles manquantes depuis une sauvegarde chiffree des cles * (si la sauvegarde est bien valide) * * @param string $backup Sauvegarde chiffrée (générée par backup()) * @param int $id_auteur * @param string $pass * @return void */ public function restore( string $backup, #[\SensitiveParameter] string $password_clair, #[\SensitiveParameter] string $password_hash, int $id_auteur ): bool { if (empty($backup)) { return false; } $sauvegarde = Chiffrement::dechiffrer($backup, $password_clair); $json = json_decode($sauvegarde, true); if (!$json) { return false; } // cela semble une sauvegarde valide $cles_potentielles = array_map('base64_decode', $json); // il faut faire une double verif sur secret_des_auth // pour s'assurer qu'elle permet bien de decrypter le pass de l'auteur qui fournit la sauvegarde // et par extension tous les passwords if (!empty($cles_potentielles['secret_des_auth'])) { if (!Password::verifier($password_clair, $password_hash, $cles_potentielles['secret_des_auth'])) { spip_log("Restauration de la cle `secret_des_auth` par id_auteur $id_auteur erronnee, on ignore", 'chiffrer' . _LOG_INFO_IMPORTANTE); unset($cles_potentielles['secret_des_auth']); } } // on merge les cles pour recuperer les cles manquantes $restauration = false; foreach ($cles_potentielles as $name => $key) { if (!$this->cles->has($name)) { $this->cles->set($name, $key); spip_log("Restauration de la cle $name par id_auteur $id_auteur", 'chiffrer' . _LOG_INFO_IMPORTANTE); $restauration = true; } } return $restauration; } private function getKey(string $name, bool $autoInit): ?string { if ($this->cles->has($name)) { return $this->cles->get($name); } if ($autoInit) { $this->cles->generate($name); // si l'ecriture de fichier a bien marche on peut utiliser la cle if ($this->save()) { return $this->cles->get($name); } // sinon loger et annule la cle generee car il ne faut pas l'utiliser spip_log('Echec ecriture du fichier cle ' . $this->file . " ; impossible de generer une cle $name", 'chiffrer' . _LOG_ERREUR); $this->cles->delete($name); } return null; } private function getMetaKey(string $name, bool $autoInit = true): ?string { if (!isset($GLOBALS['meta'][$name])) { include_spip('base/abstract_sql'); $GLOBALS['meta'][$name] = sql_getfetsel('valeur', 'spip_meta', 'nom = ' . sql_quote($name, '', 'string')); } $key = base64_decode($GLOBALS['meta'][$name] ?? ''); if (strlen($key) === \SODIUM_CRYPTO_SECRETBOX_KEYBYTES) { return $key; } if (!$autoInit) { return null; } $key = Chiffrement::keygen(); ecrire_meta($name, base64_encode($key), 'non'); lire_metas(); // au cas ou ecrire_meta() ne fonctionne pas return $key; } private function read(): array { lire_fichier_securise($this->file, $json); if ( $json and $json = \json_decode($json, true) and is_array($json) ) { return array_map('base64_decode', $json); } return []; } }