PhPsst::generateKey()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 10
nc 3
nop 0
dl 0
loc 14
ccs 10
cts 10
cp 1
crap 3
rs 9.9332
c 0
b 0
f 0
1
<?php
2
/**
3
 * PhPsst.
4
 *
5
 * @copyright Copyright (c) 2018 Felix Sandström
6
 * @license   MIT
7
 */
8
9
namespace PhPsst;
10
11
use Illuminate\Encryption\Encrypter;
12
use PhPsst\Storage\Storage;
13
14
/**
15
 * A PHP library for distributing (one time) passwords/secrets in a more secure way.
16
 *
17
 * @author Felix Sandström <http://github.com/felixsand>
18
 */
19
class PhPsst
20
{
21
    /**
22
     * @var Storage
23
     */
24
    protected $storage;
25
26
    /**
27
     * @var string
28
     */
29
    protected $cipher;
30
31
    /**
32
     * @const string
33
     */
34
    public const CIPHER_DEFAULT = 'AES-256-CBC';
35
36 5
    public function __construct(Storage $storage, string $cipher = null)
37
    {
38 5
        $this->storage = $storage;
39 5
        if ($cipher !== null) {
40 1
            $this->cipher = $cipher;
41
        } else {
42 5
            $this->cipher = self::CIPHER_DEFAULT;
43
        }
44
    }
45
46 9
    public function store(string $password, int $ttl = 3600, int $views = 1): string
47
    {
48 9
        if (empty($password)) {
49 1
            throw new \InvalidArgumentException('The password has to be set');
50
        }
51
52 8
        if ($ttl < 1) {
53 1
            throw new \InvalidArgumentException('TTL has to be higher than 0');
54
        }
55
56 7
        if ($views < 1) {
57 1
            throw new \InvalidArgumentException('Views has to be highter han 0');
58
        }
59
60 6
        $id = uniqid('', false);
61 6
        $key = $this->generateKey();
62 5
        $encrypter = new Encrypter($key, $this->cipher);
63
64 5
        $this->storage->store(new Password($id, $encrypter->encrypt($password), ($ttl + time()), $views));
65
66 5
        return $id . ';' . $key;
67
    }
68
69 6
    public function retrieve(string $secret): string
70
    {
71 6
        $idKeyArray = explode(';', $secret);
72 6
        if (\count($idKeyArray) !== 2) {
73 1
            throw new \InvalidArgumentException('Invalid secret');
74
        }
75 5
        [$id, $key] = $idKeyArray;
76 5
        $id = preg_replace("/[^a-zA-Z\d]/", '', $id);
77
78 5
        if (!($password = $this->storage->get($id))) {
79 3
            throw new PhPsstException('No password with that ID found', PhPsstException::NO_PASSWORD_WITH_ID_FOUND);
80
        }
81 4
        $encrypter = new Encrypter($key, $this->cipher);
82
83 4
        $password->decreaseViews();
84 4
        if ($password->getViews() > 0) {
85 2
            $this->storage->store($password, true);
86
        } else {
87 2
            $this->storage->delete($password);
88
        }
89
90 4
        return $encrypter->decrypt($password->getPassword());
91
    }
92
93 3
    protected function generateKey(): string
94
    {
95 3
        switch ($this->cipher) {
96 3
            case 'AES-128-CBC':
97 1
                $key = bin2hex(random_bytes(8));
98 1
                break;
99 2
            case 'AES-256-CBC':
100 1
                $key = bin2hex(random_bytes(16));
101 1
                break;
102
            default:
103 1
                throw new \RuntimeException('Only supported ciphers are AES-128-CBC and AES-256-CBC');
104
        }
105
106 2
        return $key;
107
    }
108
}
109