@mp-lb/doctrine-secrets 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -0
- package/dist/base64ToBytes.d.ts +1 -0
- package/dist/base64ToBytes.js +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +7 -0
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +3 -0
- package/dist/cryptoTypes.d.ts +3 -0
- package/dist/cryptoTypes.js +1 -0
- package/dist/decryptBytes.d.ts +6 -0
- package/dist/decryptBytes.js +3 -0
- package/dist/decryptDoctrineEncryptedFile.d.ts +2 -0
- package/dist/decryptDoctrineEncryptedFile.js +19 -0
- package/dist/decryptDoctrineEncryptedFileText.d.ts +2 -0
- package/dist/decryptDoctrineEncryptedFileText.js +3 -0
- package/dist/importAesKey.d.ts +2 -0
- package/dist/importAesKey.js +2 -0
- package/dist/importRecipientSecretKey.d.ts +2 -0
- package/dist/importRecipientSecretKey.js +9 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +4 -0
- package/dist/parseDoctrineEncryptedFilePayload.d.ts +2 -0
- package/dist/parseDoctrineEncryptedFilePayload.js +12 -0
- package/dist/readEncryptedFile.d.ts +1 -0
- package/dist/readEncryptedFile.js +2 -0
- package/dist/readRecipientSecretFromEnv.d.ts +1 -0
- package/dist/readRecipientSecretFromEnv.js +7 -0
- package/dist/runCli.d.ts +1 -0
- package/dist/runCli.js +19 -0
- package/dist/types.d.ts +22 -0
- package/dist/types.js +1 -0
- package/dist/unwrapFileKeyBytes.d.ts +6 -0
- package/dist/unwrapFileKeyBytes.js +16 -0
- package/dist/usage.d.ts +1 -0
- package/dist/usage.js +8 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Doctrine Secrets
|
|
2
|
+
|
|
3
|
+
Decrypt Doctrine encrypted text files from CI or other Node.js runtimes.
|
|
4
|
+
|
|
5
|
+
## CLI
|
|
6
|
+
|
|
7
|
+
Store the external recipient secret in your CI secret manager:
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
doctrine-secrets decrypt secrets.enc.json > secrets.json
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The CLI reads the recipient secret from `DOCTRINE_RECIPIENT_SECRET` by default.
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
DOCTRINE_RECIPIENT_SECRET='doctrine-recipient-v1_...' doctrine-secrets decrypt secrets.enc.json
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Library
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { decryptDoctrineEncryptedFileText } from "@mp-lb/doctrine-secrets";
|
|
23
|
+
|
|
24
|
+
const plaintext = await decryptDoctrineEncryptedFileText({
|
|
25
|
+
encryptedText,
|
|
26
|
+
recipientSecret: process.env.DOCTRINE_RECIPIENT_SECRET!,
|
|
27
|
+
});
|
|
28
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const base64ToBytes: (value: string) => Uint8Array;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const base64ToBytes = (value) => new Uint8Array(Buffer.from(value, "base64"));
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { encryptedFileCipher } from "./constants.js";
|
|
2
|
+
import { base64ToBytes } from "./base64ToBytes.js";
|
|
3
|
+
export const decryptBytes = async ({ data, iv, key, }) => new Uint8Array(await crypto.subtle.decrypt({ iv: base64ToBytes(iv), name: encryptedFileCipher }, key, base64ToBytes(data)));
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { decryptBytes } from "./decryptBytes.js";
|
|
2
|
+
import { importAesKey } from "./importAesKey.js";
|
|
3
|
+
import { importRecipientSecretKey } from "./importRecipientSecretKey.js";
|
|
4
|
+
import { parseDoctrineEncryptedFilePayload } from "./parseDoctrineEncryptedFilePayload.js";
|
|
5
|
+
import { unwrapFileKeyBytes } from "./unwrapFileKeyBytes.js";
|
|
6
|
+
export const decryptDoctrineEncryptedFile = async ({ encryptedText, recipientSecret, }) => {
|
|
7
|
+
const payload = parseDoctrineEncryptedFilePayload(encryptedText);
|
|
8
|
+
const recipientKey = await importRecipientSecretKey(recipientSecret);
|
|
9
|
+
const fileKeyBytes = await unwrapFileKeyBytes({
|
|
10
|
+
recipientKey,
|
|
11
|
+
recipients: payload.recipients,
|
|
12
|
+
});
|
|
13
|
+
const fileKey = await importAesKey(fileKeyBytes, ["decrypt"]);
|
|
14
|
+
return decryptBytes({
|
|
15
|
+
data: payload.data,
|
|
16
|
+
iv: payload.iv,
|
|
17
|
+
key: fileKey,
|
|
18
|
+
});
|
|
19
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { doctrineRecipientSecretPrefix } from "./constants.js";
|
|
2
|
+
import { base64ToBytes } from "./base64ToBytes.js";
|
|
3
|
+
import { importAesKey } from "./importAesKey.js";
|
|
4
|
+
export const importRecipientSecretKey = async (recipientSecret) => {
|
|
5
|
+
if (!recipientSecret.startsWith(doctrineRecipientSecretPrefix)) {
|
|
6
|
+
throw new Error("Unsupported Doctrine recipient secret.");
|
|
7
|
+
}
|
|
8
|
+
return importAesKey(base64ToBytes(recipientSecret.slice(doctrineRecipientSecretPrefix.length)), ["decrypt"]);
|
|
9
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { doctrineRecipientSecretPrefix, encryptedFileCipher, encryptedFileVersion, } from "./constants";
|
|
2
|
+
export { decryptDoctrineEncryptedFile } from "./decryptDoctrineEncryptedFile";
|
|
3
|
+
export { decryptDoctrineEncryptedFileText } from "./decryptDoctrineEncryptedFileText";
|
|
4
|
+
export { parseDoctrineEncryptedFilePayload } from "./parseDoctrineEncryptedFilePayload";
|
|
5
|
+
export type { DecryptDoctrineEncryptedFileInput, DoctrineEncryptedFilePayload, DoctrineEncryptedRecipient, DoctrineEncryptedValue, } from "./types";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { doctrineRecipientSecretPrefix, encryptedFileCipher, encryptedFileVersion, } from "./constants.js";
|
|
2
|
+
export { decryptDoctrineEncryptedFile } from "./decryptDoctrineEncryptedFile.js";
|
|
3
|
+
export { decryptDoctrineEncryptedFileText } from "./decryptDoctrineEncryptedFileText.js";
|
|
4
|
+
export { parseDoctrineEncryptedFilePayload } from "./parseDoctrineEncryptedFilePayload.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { encryptedFileCipher, encryptedFileVersion } from "./constants.js";
|
|
2
|
+
export const parseDoctrineEncryptedFilePayload = (encryptedText) => {
|
|
3
|
+
const parsed = JSON.parse(encryptedText);
|
|
4
|
+
if (parsed.doctrineEncryptedFile !== encryptedFileVersion ||
|
|
5
|
+
parsed.cipher !== encryptedFileCipher ||
|
|
6
|
+
typeof parsed.data !== "string" ||
|
|
7
|
+
typeof parsed.iv !== "string" ||
|
|
8
|
+
!Array.isArray(parsed.recipients)) {
|
|
9
|
+
throw new Error("Unsupported Doctrine encrypted file format.");
|
|
10
|
+
}
|
|
11
|
+
return parsed;
|
|
12
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const readEncryptedFile: (filePath: string) => Promise<string>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const readRecipientSecretFromEnv: () => string;
|
package/dist/runCli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const runCli: (args: string[]) => Promise<void>;
|
package/dist/runCli.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { decryptDoctrineEncryptedFileText } from "./decryptDoctrineEncryptedFileText.js";
|
|
2
|
+
import { readEncryptedFile } from "./readEncryptedFile.js";
|
|
3
|
+
import { readRecipientSecretFromEnv } from "./readRecipientSecretFromEnv.js";
|
|
4
|
+
import { usage } from "./usage.js";
|
|
5
|
+
export const runCli = async (args) => {
|
|
6
|
+
const [command, filePath] = args;
|
|
7
|
+
if (command === "--help" || command === "-h") {
|
|
8
|
+
process.stdout.write(usage);
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
if (command !== "decrypt" || !filePath) {
|
|
12
|
+
throw new Error(usage);
|
|
13
|
+
}
|
|
14
|
+
const plaintext = await decryptDoctrineEncryptedFileText({
|
|
15
|
+
encryptedText: await readEncryptedFile(filePath),
|
|
16
|
+
recipientSecret: readRecipientSecretFromEnv(),
|
|
17
|
+
});
|
|
18
|
+
process.stdout.write(plaintext);
|
|
19
|
+
};
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { encryptedFileCipher, encryptedFileVersion } from "./constants";
|
|
2
|
+
export type DoctrineEncryptedValue = {
|
|
3
|
+
cipher: typeof encryptedFileCipher;
|
|
4
|
+
data: string;
|
|
5
|
+
iv: string;
|
|
6
|
+
};
|
|
7
|
+
export type DoctrineEncryptedRecipient = {
|
|
8
|
+
label?: string;
|
|
9
|
+
recipientId: string;
|
|
10
|
+
wrappedDataKey: DoctrineEncryptedValue;
|
|
11
|
+
};
|
|
12
|
+
export type DoctrineEncryptedFilePayload = {
|
|
13
|
+
cipher: typeof encryptedFileCipher;
|
|
14
|
+
data: string;
|
|
15
|
+
doctrineEncryptedFile: typeof encryptedFileVersion;
|
|
16
|
+
iv: string;
|
|
17
|
+
recipients: DoctrineEncryptedRecipient[];
|
|
18
|
+
};
|
|
19
|
+
export type DecryptDoctrineEncryptedFileInput = {
|
|
20
|
+
encryptedText: string;
|
|
21
|
+
recipientSecret: string;
|
|
22
|
+
};
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { DoctrineEncryptedRecipient } from "./types";
|
|
2
|
+
import type { AesCryptoKey } from "./cryptoTypes";
|
|
3
|
+
export declare const unwrapFileKeyBytes: ({ recipientKey, recipients, }: {
|
|
4
|
+
recipientKey: AesCryptoKey;
|
|
5
|
+
recipients: DoctrineEncryptedRecipient[];
|
|
6
|
+
}) => Promise<Uint8Array>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { decryptBytes } from "./decryptBytes.js";
|
|
2
|
+
export const unwrapFileKeyBytes = async ({ recipientKey, recipients, }) => {
|
|
3
|
+
for (const recipient of recipients) {
|
|
4
|
+
try {
|
|
5
|
+
return await decryptBytes({
|
|
6
|
+
data: recipient.wrappedDataKey.data,
|
|
7
|
+
iv: recipient.wrappedDataKey.iv,
|
|
8
|
+
key: recipientKey,
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
// Only one recipient should match a given external recipient secret.
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
throw new Error("No recipient matched the Doctrine recipient secret.");
|
|
16
|
+
};
|
package/dist/usage.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const usage = "Doctrine Secrets\n\nUsage:\n doctrine-secrets decrypt <secrets.enc.json>\n\nEnvironment:\n DOCTRINE_RECIPIENT_SECRET External recipient secret for the encrypted file\n";
|
package/dist/usage.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mp-lb/doctrine-secrets",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/mp-lb/hyperstore.git",
|
|
17
|
+
"directory": "packages/doctrine-secrets"
|
|
18
|
+
},
|
|
19
|
+
"bin": {
|
|
20
|
+
"doctrine-secrets": "dist/cli.js"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"README.md",
|
|
25
|
+
"package.json"
|
|
26
|
+
],
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
},
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=20"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "rm -rf dist && tsc --project tsconfig.build.json && node scripts/fix-esm-imports.mjs && chmod +x dist/cli.js",
|
|
35
|
+
"dev": "tsc --watch --project tsconfig.json",
|
|
36
|
+
"typecheck": "tsc --noEmit",
|
|
37
|
+
"test": "vitest run"
|
|
38
|
+
}
|
|
39
|
+
}
|