48 static private $path = null;
50 public function __construct()
52 if (null === self::$path) {
53 $registryPath = getenv(
'CRYPTAL_REGISTRY');
54 if (
false === $registryPath ||
'' === $registryPath) {
55 self::$path = __DIR__ . DIRECTORY_SEPARATOR .
'registry.dat';
57 self::$path = $registryPath;
64 public static function getInstance()
66 static $instance = null;
67 if (null === $instance) {
68 $instance =
new static;
75 $ifaces = class_implements($cls);
76 $iface =
'fpoirotte\\Cryptal\\Implementers\\CryptoInterface';
77 if (!$ifaces || !in_array($iface, $ifaces)) {
78 throw new \InvalidArgumentException(
"$cls does not implement $iface");
80 $this->metadata[
'crypt'][
"$cipher:$mode"][] = array($packageName, $cls, $type);
86 $ifaces = class_implements($cls);
87 $iface =
'fpoirotte\\Cryptal\\Implementers\\HashInterface';
88 if (!$ifaces || !in_array($iface, $ifaces)) {
89 throw new \InvalidArgumentException(
"$cls does not implement $iface");
91 $this->metadata[
'hash'][
"$algo"][] = array($packageName, $cls, $type);
97 $ifaces = class_implements($cls);
98 $iface =
'fpoirotte\\Cryptal\\Implementers\\MacInterface';
99 if (!$ifaces || !in_array($iface, $ifaces)) {
100 throw new \InvalidArgumentException(
"$cls does not implement $iface");
102 $this->metadata[
'mac'][
"$algo"][] = array($packageName, $cls, $type);
106 public function removeAlgorithms($packageName)
108 foreach ($this->metadata as &$algoTypes) {
109 foreach ($algoTypes as &$algos) {
110 foreach ($algos as $key => $desc) {
111 if ($desc[0] === $packageName) {
120 public function load($registerDefaultAlgorithms =
true)
122 $data = @file_get_contents(self::$path);
123 if (
false === $data) {
126 $this->metadata = unserialize($data);
127 $this->removeAlgorithms(
'');
130 if ($registerDefaultAlgorithms) {
131 $this->registerDefaultAlgorithms();
136 public function registerDefaultAlgorithms()
141 'fpoirotte\\Cryptal\\DefaultAlgorithms\\ChaCha20Openssh',
142 CipherEnum::CIPHER_CHACHA20_OPENSSH(),
143 ModeEnum::MODE_ECB(),
144 ImplementationTypeEnum::TYPE_USERLAND()
148 'fpoirotte\\Cryptal\\DefaultAlgorithms\\ChaCha20',
149 CipherEnum::CIPHER_CHACHA20(),
150 ModeEnum::MODE_ECB(),
151 ImplementationTypeEnum::TYPE_USERLAND()
154 CipherEnum::CIPHER_CAMELIA_128(),
155 CipherEnum::CIPHER_CAMELIA_192(),
156 CipherEnum::CIPHER_CAMELIA_256(),
158 foreach ($camellia as $cipher) {
161 'fpoirotte\\Cryptal\\DefaultAlgorithms\\Camellia',
163 ModeEnum::MODE_ECB(),
164 ImplementationTypeEnum::TYPE_USERLAND()
170 HashEnum::HASH_MD5(),
171 HashEnum::HASH_SHA1(),
173 foreach ($algos as $algo) {
176 'fpoirotte\\Cryptal\\DefaultAlgorithms\\Hash',
178 ImplementationTypeEnum::TYPE_COMPILED()
185 'fpoirotte\\Cryptal\\DefaultAlgorithms\\Cmac',
187 ImplementationTypeEnum::TYPE_USERLAND()
191 'fpoirotte\\Cryptal\\DefaultAlgorithms\\Poly1305',
192 MacEnum::MAC_POLY1305(),
193 ImplementationTypeEnum::TYPE_USERLAND()
196 MacEnum::MAC_UMAC_32(),
197 MacEnum::MAC_UMAC_64(),
198 MacEnum::MAC_UMAC_96(),
199 MacEnum::MAC_UMAC_128(),
201 foreach ($algos as $algo) {
204 'fpoirotte\\Cryptal\\DefaultAlgorithms\\Umac',
206 ImplementationTypeEnum::TYPE_USERLAND()
213 public function save()
215 file_put_contents(self::$path, serialize($this->metadata));
219 public function reset()
221 $this->metadata = array(
229 protected static function findCipher(
CipherEnum $cipher,
ModeEnum $mode, $allowUnsafe)
231 $registry = self::getInstance();
233 (
string) ImplementationTypeEnum::TYPE_ASSEMBLY() => null,
234 (
string) ImplementationTypeEnum::TYPE_COMPILED() => null,
235 (
string) ImplementationTypeEnum::TYPE_USERLAND() => null,
238 if (empty($registry->metadata[
'crypt'][
"$cipher:$mode"])) {
239 throw new \Exception(
'Unsupported cipher/mode combination');
242 foreach ($registry->metadata[
'crypt'][
"$cipher:$mode"] as $impl) {
243 $res[
"${impl[2]}"] = $impl[1];
246 foreach ($res as $type => $cls) {
248 if ($type == (
string) ImplementationTypeEnum::TYPE_USERLAND() && !$allowUnsafe) {
249 throw new \Exception(
'No safe implementation found for cipher/mode');
256 throw new \Exception(
'Unsupported cipher/mode combination');
259 public static function buildCipher(
264 $tagLength = CryptoInterface::DEFAULT_TAG_LENGTH,
267 if (!is_string($key)) {
268 throw new \InvalidArgumentException(
'Invalid key');
271 if (!is_int($tagLength) || 0 > $tagLength) {
272 throw new \InvalidArgumentException(
'Invalid tag length');
275 $cls = self::findCipher($cipher, $mode, $allowUnsafe);
276 return new $cls($cipher, $mode, $padding, $key, $tagLength);
279 protected static function findHash(
HashEnum $algo, $allowUnsafe)
281 $registry = self::getInstance();
283 (
string) ImplementationTypeEnum::TYPE_ASSEMBLY() => null,
284 (
string) ImplementationTypeEnum::TYPE_COMPILED() => null,
285 (
string) ImplementationTypeEnum::TYPE_USERLAND() => null,
288 if (empty($registry->metadata[
'hash'][
"$algo"])) {
289 throw new \Exception(
'Unsupported hash algorithm');
292 foreach ($registry->metadata[
'hash'][
"$algo"] as $impl) {
293 $res[
"${impl[2]}"] = $impl[1];
296 foreach ($res as $type => $cls) {
298 if ($type == (
string) ImplementationTypeEnum::TYPE_USERLAND() && !$allowUnsafe) {
299 throw new \Exception(
'No safe implementation found for hash');
306 throw new \Exception(
'Unsupported hash algorithm');
309 public static function buildHash(
HashEnum $algo, $allowUnsafe =
false)
311 $cls = self::findHash($algo, $allowUnsafe);
312 return new $cls($algo);
315 protected static function findMac(
MacEnum $algo, $allowUnsafe)
317 $registry = self::getInstance();
320 (
string) ImplementationTypeEnum::TYPE_ASSEMBLY() => null,
321 (
string) ImplementationTypeEnum::TYPE_COMPILED() => null,
322 (
string) ImplementationTypeEnum::TYPE_USERLAND() => null,
325 if (empty($registry->metadata[
'mac'][
"$algo"])) {
326 throw new \Exception(
'Unsupported MAC algorithm');
329 foreach ($registry->metadata[
'mac'][
"$algo"] as $impl) {
330 $res[
"${impl[2]}"] = $impl[1];
333 foreach ($res as $type => $cls) {
335 if ($type == (
string) ImplementationTypeEnum::TYPE_USERLAND() && !$allowUnsafe) {
336 throw new \Exception(
'No safe implementation found for MAC');
343 throw new \Exception(
'Unsupported MAC algorithm');
346 public static function buildMac(
353 if (!is_string($key)) {
354 throw new \InvalidArgumentException(
'Invalid key');
357 if (!is_string($nonce)) {
358 throw new \InvalidArgumentException(
'Invalid nonce');
362 self::findHash($subAlgo, $allowUnsafe);
364 self::findCipher($subAlgo, ModeEnum::MODE_ECB(), $allowUnsafe);
366 throw new \InvalidArgumentException(
'Invalid inner algorithm');
369 $cls = self::findMac($algo, $allowUnsafe);
370 return new $cls($algo, $subAlgo, $key, $nonce);
373 public function getSupportedCiphers()
376 foreach ($this->metadata[
'crypt'] as $algo => $dummy) {
377 list($cipher, $mode) = explode(
':', $algo);
378 $res[] = array(CipherEnum::$cipher(), ModeEnum::$mode());
383 public function getSupportedHashes()
386 foreach ($this->metadata[
'hash'] as $algo => $dummy) {
387 $res[] = HashEnum::$algo();
392 public function getSupportedMacs()
395 foreach ($this->metadata[
'mac'] as $algo => $dummy) {
396 $res[] = MacEnum::$algo();