@nomicfoundation/hardhat-keystore
Version:
A module for managing keystore files that store a map from IDs to encrypted string values.
140 lines (121 loc) • 3.58 kB
text/typescript
import type { Keystore as KeystoreI } from "../types.js";
import {
assertHardhatInvariant,
HardhatError,
} from "@nomicfoundation/hardhat-errors";
import {
addSecretToKeystore,
decryptSecret,
doesKeyExist,
HmacKeyDecryptionError,
removeSecretFromKeystore,
validateHmac,
type EncryptedKeystore,
} from "./encryption.js";
export class Keystore implements KeystoreI {
#keystoreData: EncryptedKeystore;
constructor(keystoreData: EncryptedKeystore) {
this.#keystoreData = keystoreData;
}
public toJSON(): EncryptedKeystore {
return this.#keystoreData;
}
public async listUnverifiedKeys(): Promise<string[]> {
// In this scenario the keystore is not validated for integrity, so the returned keys might have been tampered with.
// This is acceptable if the keys are only listed for display purposes.
// This risk is considered acceptable for this use case.
return Object.keys(this.#keystoreData.secrets);
}
public async hasKey(key: string, masterKey: Uint8Array): Promise<boolean> {
try {
return doesKeyExist({
masterKey,
encryptedKeystore: this.#keystoreData,
key,
});
} catch (error) {
if (error instanceof HmacKeyDecryptionError) {
throw new HardhatError(
HardhatError.ERRORS.HARDHAT_KEYSTORE.GENERAL.INVALID_PASSWORD_OR_CORRUPTED_KEYSTORE,
);
}
throw error;
}
}
public async readValue(key: string, masterKey: Uint8Array): Promise<string> {
assertHardhatInvariant(
key in this.#keystoreData.secrets,
"Unknown key should never be read",
);
try {
return decryptSecret({
masterKey,
encryptedKeystore: this.#keystoreData,
key,
});
} catch (error) {
if (error instanceof HmacKeyDecryptionError) {
throw new HardhatError(
HardhatError.ERRORS.HARDHAT_KEYSTORE.GENERAL.INVALID_PASSWORD_OR_CORRUPTED_KEYSTORE,
);
}
throw error;
}
}
public async removeKey(key: string, masterKey: Uint8Array): Promise<void> {
assertHardhatInvariant(
key in this.#keystoreData.secrets,
"Unknown key should never be removed",
);
try {
this.#keystoreData = removeSecretFromKeystore({
masterKey,
encryptedKeystore: this.#keystoreData,
keyToRemove: key,
});
} catch (error) {
if (error instanceof HmacKeyDecryptionError) {
throw new HardhatError(
HardhatError.ERRORS.HARDHAT_KEYSTORE.GENERAL.INVALID_PASSWORD_OR_CORRUPTED_KEYSTORE,
);
}
throw error;
}
}
public async addNewValue(
key: string,
value: string,
masterKey: Uint8Array,
): Promise<void> {
try {
this.#keystoreData = addSecretToKeystore({
masterKey,
encryptedKeystore: this.#keystoreData,
key,
value,
});
} catch (error) {
if (error instanceof HmacKeyDecryptionError) {
throw new HardhatError(
HardhatError.ERRORS.HARDHAT_KEYSTORE.GENERAL.INVALID_PASSWORD_OR_CORRUPTED_KEYSTORE,
);
}
throw error;
}
}
public async isValidPassword(masterKey: Uint8Array): Promise<void> {
try {
validateHmac({
masterKey,
encryptedKeystore: this.#keystoreData,
});
} catch (error) {
if (error instanceof HmacKeyDecryptionError) {
throw new HardhatError(
HardhatError.ERRORS.HARDHAT_KEYSTORE.GENERAL.INVALID_PASSWORD_OR_CORRUPTED_KEYSTORE,
);
}
throw error;
}
}
}