File size: 2,775 Bytes
d2897cd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
<?php

namespace Mautic\CoreBundle\Helper;

use Mautic\CoreBundle\Security\Cryptography\Cipher\Symmetric\SymmetricCipherInterface;
use Mautic\CoreBundle\Security\Exception\Cryptography\Symmetric\InvalidDecryptionException;

class EncryptionHelper
{
    /**
     * @var SymmetricCipherInterface[]
     */
    private ?array $availableCiphers = null;

    /**
     * @var string
     */
    private $key;

    public function __construct(
        CoreParametersHelper $coreParametersHelper
    ) {
        $nonCipherArgs = 1;
        for ($i = $nonCipherArgs; $i < func_num_args(); ++$i) {
            $possibleCipher = func_get_arg($i);
            if (!($possibleCipher instanceof SymmetricCipherInterface)) {
                throw new \InvalidArgumentException($possibleCipher::class.' has to implement '.SymmetricCipherInterface::class);
            }
            if (!$possibleCipher->isSupported()) {
                continue;
            }
            $this->availableCiphers[] = $possibleCipher;
        }

        if (!$this->availableCiphers || 0 === count($this->availableCiphers)) {
            throw new \RuntimeException('None of possible cryptography libraries is supported');
        }

        $this->key = $coreParametersHelper->get('mautic.secret_key');
    }

    /**
     * Returns a 64 character string.
     */
    public static function generateKey(): string
    {
        return hash('sha256', uniqid(mt_rand(), true));
    }

    /**
     * Encrypt string.
     *
     * @param mixed $data
     */
    public function encrypt($data): string
    {
        $encryptionCipher = reset($this->availableCiphers);
        $initVector       = $encryptionCipher->getRandomInitVector();
        $encrypted        = $encryptionCipher->encrypt(serialize($data), $this->key, $initVector);

        return base64_encode($encrypted).'|'.base64_encode($initVector);
    }

    /**
     * Decrypt string.
     * Returns false in case of failed decryption.
     *
     * @param string $data
     * @param bool   $mainDecryptOnly
     *
     * @return mixed|false
     */
    public function decrypt($data, $mainDecryptOnly = false)
    {
        $encryptData      = explode('|', $data);
        $encryptedMessage = base64_decode($encryptData[0]);
        $initVector       = base64_decode($encryptData[1]);
        $mainTried        = false;
        foreach ($this->availableCiphers as $availableCipher) {
            if ($mainDecryptOnly && $mainTried) {
                return false;
            }
            try {
                return Serializer::decode($availableCipher->decrypt($encryptedMessage, $this->key, $initVector));
            } catch (InvalidDecryptionException) {
            }
            $mainTried = true;
        }

        return false;
    }
}