Skip to content

Commit 1c2d3de

Browse files
committed
add decryption of vault
1 parent f532572 commit 1c2d3de

File tree

3 files changed

+125
-13
lines changed

3 files changed

+125
-13
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ phpstan.tests.neon
1212
phpunit.xml
1313
vendor
1414

15-
index.php
15+
.vscode

composer.json

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,39 @@
11
{
2+
"name": "dotenv-org/phpdotenv-vault",
3+
"description": "Loads environment variables from `.env` or `.env.vault`",
4+
"keywords": [
5+
"env",
6+
"dotenv",
7+
"environment",
8+
"environment variables",
9+
"dotenv-vault",
10+
"configurations"
11+
],
12+
"license": "BSD-3-Clause",
13+
"autoload": {
14+
"psr-4": {
15+
"PhpdotenvVault\\": "src/"
16+
}
17+
},
18+
"authors": [
19+
{
20+
"name": "dotenv",
21+
"email": "[email protected]",
22+
"homepage": "https://github.com/dotenv-org"
23+
}
24+
],
225
"require": {
26+
"php": "^7.1.3 || ^8.0",
327
"vlucas/phpdotenv": "^5.5"
28+
},
29+
"extra": {
30+
"laravel": {
31+
"providers": [
32+
"PhpdotenvVault\\PhpdotenvVaultServiceProvider"
33+
]
34+
}
35+
},
36+
"require-dev": {
37+
"orchestra/testbench": "^3.8"
438
}
5-
}
39+
}

src/DotenvVault.php

Lines changed: 89 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,25 @@
22

33
namespace DotenvVault;
44
use Dotenv\Dotenv;
5-
use Dotenv\Exception\InvalidPathException;
65
use Dotenv\Loader\Loader;
76
use Dotenv\Loader\LoaderInterface;
87
use Dotenv\Parser\Parser;
98
use Dotenv\Parser\ParserInterface;
10-
use Dotenv\Repository\Adapter\ArrayAdapter;
11-
use Dotenv\Repository\Adapter\PutenvAdapter;
129
use Dotenv\Repository\RepositoryBuilder;
1310
use Dotenv\Repository\RepositoryInterface;
1411
use Dotenv\Store\StoreBuilder;
1512
use Dotenv\Store\StoreInterface;
16-
use Dotenv\Store\StringStore;
13+
use Exception;
14+
15+
class DotEnvVaultError extends Exception { }
1716

1817
class DotEnvVault extends Dotenv
1918
{
2019
private $store;
2120
private $parser;
2221
private $loader;
2322
private $repository;
23+
private $dotenv_key;
2424
public function __construct(
2525
StoreInterface $store,
2626
ParserInterface $parser,
@@ -32,22 +32,100 @@ public function __construct(
3232
$this->parser = $parser;
3333
$this->loader = $loader;
3434
$this->repository = $repository;
35-
// parent::__construct($store, $parser, $loader, $repository);
35+
}
36+
37+
public function load()
38+
{
39+
$this->dotenv_key = getenv("DOTENV_KEY");
40+
if ($this->dotenv_key !== false){
41+
42+
$entries = $this->parser->parse($this->store->read());
43+
$this->loader->load($this->repository, $entries);
44+
45+
$plaintext = $this->parse_vault();
3646

47+
// parsing plaintext and loading to $_ENV
48+
$test_entries = $this->parser->parse($plaintext);
49+
$this->loader->load($this->repository, $test_entries);
50+
}
51+
else {
52+
$entries = $this->parser->parse($this->store->read());
53+
54+
var_dump($entries[0]->getName());
55+
var_dump($entries[0]->getValue());
56+
57+
return $this->loader->load($this->repository, $entries);
58+
}
3759
}
3860

39-
public function load_vault()
61+
public function parse_vault()
4062
{
41-
echo "\n====LOAD FUNCTION====\n";
42-
$entries = $this->parser->parse($this->store->read());
63+
$dotenv_keys = explode(',', $this->dotenv_key);
64+
$keys = array();
65+
foreach($dotenv_keys as $key)
66+
{
67+
// parse DOTENV_KEY, format is a URI.
68+
$uri = parse_url(trim($key));
69+
70+
// get encrypted key
71+
$pass = $uri['pass'];
72+
73+
// Get environment from query params.
74+
parse_str($uri['query'], $params);
75+
$vault_environment = $params['environment'] or throw new DotEnvVaultError('INVALID_DOTENV_KEY: Missing environment part.');
4376

44-
return $this->loader->load($this->repository, $entries);
77+
# Getting ciphertext from correct environment in .env.vault
78+
$vault_environment = strtoupper($vault_environment);
79+
$environment_key = "DOTENV_VAULT_{$vault_environment}";
4580

81+
$ciphertext = $_ENV["{$environment_key}"] or throw new DotEnvVaultError("NOT_FOUND_DOTENV_ENVIRONMENT: Cannot locate environment {$environment_key} in your .env.vault file. Run 'npx dotenv-vault build' to include it.");
82+
83+
array_push($keys, array('encrypted_key' => $pass, 'ciphertext' => $ciphertext));
84+
}
85+
return $this->key_rotation($keys);
4686
}
4787

48-
public static function createImmutable($paths, $names = null, bool $shortCircuit = true, string $fileEncoding = null)
88+
private function key_rotation($keys){
89+
$count = count($keys);
90+
foreach($keys as $index=>$value) {
91+
$decrypt = $this->decrypt($value['ciphertext'], $value['encrypted_key']);
92+
93+
if ($decrypt == false && $index + 1 >= $count){
94+
throw new DotEnvVaultError('INVALID_DOTENV_KEY: Key must be valid.');
95+
}
96+
elseif($decrypt == false){
97+
continue;
98+
}
99+
else{
100+
return $decrypt;
101+
}
102+
}
103+
}
104+
105+
private function decrypt($data, $secret)
49106
{
50-
107+
$secret = hex2bin(substr($secret, 4, strlen($secret)));
108+
$data = base64_decode($data, true);
109+
$nonce = substr($data, 0, 12);
110+
$tag = substr($data, -16);
111+
$ciphertext = substr($data, 12, -16);
112+
113+
try {
114+
return openssl_decrypt(
115+
$ciphertext,
116+
'aes-256-gcm',
117+
$secret,
118+
OPENSSL_RAW_DATA,
119+
$nonce,
120+
$tag
121+
);
122+
} catch (Exception $e) {
123+
return false;
124+
}
125+
}
126+
127+
public static function createImmutable($paths, $names = null, bool $shortCircuit = true, string $fileEncoding = null)
128+
{
51129
$repository = RepositoryBuilder::createWithDefaultAdapters()->immutable()->make();
52130

53131
return self::create($repository, $paths, $names, $shortCircuit, $fileEncoding);

0 commit comments

Comments
 (0)