Skip to content
This repository was archived by the owner on Apr 13, 2025. It is now read-only.

Commit 02f0214

Browse files
committed
Document newly added functions and properties
1 parent cef8c1f commit 02f0214

File tree

2 files changed

+58
-14
lines changed

2 files changed

+58
-14
lines changed

nodecg-io-core/dashboard/crypto.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,20 @@ export async function setPassword(pw: string): Promise<boolean> {
7272
encryptedData.value = {};
7373
}
7474

75-
const salt = encryptedData.value.salt ?? cryptoJS.lib.WordArray.random(128 / 8).toString(cryptoJS.enc.Hex);
75+
const salt = encryptedData.value.salt ?? cryptoJS.lib.WordArray.random(128 / 8).toString();
76+
// Check if no salt is present, which is the case for the nodecg-io <=0.2 configs
77+
// where crypto-js derived the encryption key and managed the salt.
7678
if (encryptedData.value.salt === undefined) {
77-
const newSecret = deriveEncryptionKey(pw, salt);
79+
// Salt is unset when nodecg-io is first started.
7880

7981
if (encryptedData.value.cipherText !== undefined) {
80-
const newSecretWordArray = cryptoJS.enc.Hex.parse(newSecret);
81-
reEncryptData(encryptedData.value, pw, newSecretWordArray);
82+
// Salt is unset but we have some encrypted data.
83+
// This means that this is a old config, that we need to migrate to the new format.
84+
85+
// Re-encrypt the configuration using our own derived key instead of the password.
86+
const newEncryptionKey = deriveEncryptionKey(pw, salt);
87+
const newEncryptionKeyArr = cryptoJS.enc.Hex.parse(newEncryptionKey);
88+
reEncryptData(encryptedData.value, pw, newEncryptionKeyArr);
8289
}
8390

8491
encryptedData.value.salt = salt;

nodecg-io-core/extension/persistenceManager.ts

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,27 @@ export interface EncryptedData {
2828
* The encrypted format of the data that needs to be stored.
2929
*/
3030
cipherText?: string;
31+
32+
/**
33+
* The salt that is used when deriving the encryption key from the password.
34+
* Only set for new format with nodecg-io >=0.3.
35+
*/
3136
salt?: string;
37+
38+
/**
39+
* The initialization vector used for encryption.
40+
* Only set for new format with nodecg-io >=0.3.
41+
*/
3242
iv?: string;
3343
}
3444

3545
/**
3646
* Decrypts the passed encrypted data using the passed encryption key.
3747
* If the encryption key is wrong, an error will be returned.
3848
*
49+
* This function supports the <=0.2 format with the plain password as an
50+
* encryption key and no iv (read from ciphertext) and the >=0.3 format with the iv and derived key.
51+
*
3952
* @param cipherText the ciphertext that needs to be decrypted.
4053
* @param encryptionKey the encryption key for the encrypted data.
4154
* @param iv the initialization vector for the encrypted data.
@@ -56,18 +69,28 @@ export function decryptData(
5669
}
5770
}
5871

72+
/**
73+
* Encrypts the passed data objedt using the passed encryption key.
74+
*
75+
* @param data the data that needs to be encrypted.
76+
* @param encryptionKey the encryption key that should be used to encrypt the data.
77+
* @returns a tuple containing the encrypted data and the initialization vector as a hex string.
78+
*/
5979
export function encryptData(data: PersistentData, encryptionKey: crypto.lib.WordArray): [string, string] {
6080
const iv = crypto.lib.WordArray.random(16);
6181
const ivText = iv.toString();
6282
const encrypted = crypto.AES.encrypt(JSON.stringify(data), encryptionKey, { iv });
6383
return [encrypted.toString(), ivText];
6484
}
6585

66-
export function deriveEncryptionKey(password: string, salt: string | undefined): string {
67-
if (salt === undefined) {
68-
return password;
69-
}
70-
86+
/**
87+
* Derives a key suitable for encrypting the config from the given password.
88+
*
89+
* @param password the password from which the encryption key will be derived.
90+
* @param salt the salt that is used for key derivation.
91+
* @returns a hex encoded string of the derived key.
92+
*/
93+
export function deriveEncryptionKey(password: string, salt: string): string {
7194
const saltWordArray = crypto.enc.Hex.parse(salt);
7295

7396
return crypto
@@ -78,6 +101,14 @@ export function deriveEncryptionKey(password: string, salt: string | undefined):
78101
.toString(crypto.enc.Hex);
79102
}
80103

104+
/**
105+
* Re-encrypts the passed data to change the password/encryption key.
106+
* Currently only used to migrate from <=0.2 to >=0.3 config formats but
107+
* could be used to implement a change password feature in the future.
108+
* @param data the data that should be re-encrypted.
109+
* @param oldSecret the previous encryption key or password.
110+
* @param newSecret the new encryption key.
111+
*/
81112
export function reEncryptData(
82113
data: EncryptedData,
83114
oldSecret: string | crypto.lib.WordArray,
@@ -351,14 +382,20 @@ export class PersistenceManager {
351382
try {
352383
this.nodecg.log.info("Attempting to automatically login...");
353384

354-
const salt =
355-
this.encryptedData.value.salt ?? crypto.lib.WordArray.random(128 / 8).toString(crypto.enc.Hex);
385+
const salt = this.encryptedData.value.salt ?? crypto.lib.WordArray.random(128 / 8).toString();
386+
// Check if no salt is present, which is the case for the nodecg-io <=0.2 configs
387+
// where crypto-js derived the encryption key and managed the salt.
356388
if (this.encryptedData.value.salt === undefined) {
357-
const newSecret = deriveEncryptionKey(password, salt);
389+
// Salt is unset when nodecg-io is first started.
358390

359391
if (this.encryptedData.value.cipherText !== undefined) {
360-
const newSecretWordArray = crypto.enc.Hex.parse(newSecret);
361-
reEncryptData(this.encryptedData.value, password, newSecretWordArray);
392+
// Salt is unset but we have some encrypted data.
393+
// This means that this is a old config, that we need to migrate to the new format.
394+
395+
// Re-encrypt the configuration using our own derived key instead of the password.
396+
const newEncryptionKey = deriveEncryptionKey(password, salt);
397+
const newEncryptionKeyArr = crypto.enc.Hex.parse(newEncryptionKey);
398+
reEncryptData(this.encryptedData.value, password, newEncryptionKeyArr);
362399
}
363400

364401
this.encryptedData.value.salt = salt;

0 commit comments

Comments
 (0)