@relaycorp/webcrypto-kms
Version:
WebCrypto-compatible client for Key Management Services like GCP KMS
98 lines • 4.48 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.AwsKmsRsaPssProvider = void 0;
const client_kms_1 = require("@aws-sdk/client-kms");
const KmsRsaPssProvider_1 = require("../KmsRsaPssProvider");
const AwsKmsRsaPssPrivateKey_1 = require("./AwsKmsRsaPssPrivateKey");
const KmsError_1 = require("../KmsError");
const buffer_1 = require("../utils/buffer");
const crypto_1 = require("../utils/crypto");
// See: https://docs.aws.amazon.com/kms/latest/developerguide/asymmetric-key-specs.html
const SUPPORTED_MODULUS_LENGTHS = [2048, 3072, 4096];
const REQUEST_OPTIONS = { requestTimeout: 3_000 };
class AwsKmsRsaPssProvider extends KmsRsaPssProvider_1.KmsRsaPssProvider {
constructor(client) {
super();
this.client = client;
// See: https://docs.aws.amazon.com/kms/latest/developerguide/asymmetric-key-specs.html
this.hashAlgorithms = ['SHA-256', 'SHA-384', 'SHA-512'];
}
async onGenerateKey(algorithm) {
if (!SUPPORTED_MODULUS_LENGTHS.includes(algorithm.modulusLength)) {
throw new KmsError_1.KmsError(`Unsupported RSA modulus (${algorithm.modulusLength})`);
}
const keySpec = `RSA_${algorithm.modulusLength}`;
const command = new client_kms_1.CreateKeyCommand({
KeySpec: keySpec,
KeyUsage: client_kms_1.KeyUsageType.SIGN_VERIFY,
});
const response = await this.client.send(command, REQUEST_OPTIONS);
const keyArn = response.KeyMetadata?.Arn;
if (!keyArn) {
throw new KmsError_1.KmsError('Key creation response is missing KeyMetadata.Arn');
}
const privateKey = new AwsKmsRsaPssPrivateKey_1.AwsKmsRsaPssPrivateKey(keyArn, algorithm.hash.name, this);
const publicKeySerialised = await this.retrievePublicKey(privateKey);
const publicKey = await (0, crypto_1.derDeserialisePublicKey)(publicKeySerialised, algorithm);
return { privateKey, publicKey };
}
async onExportKey(format, key) {
requireAwsKmsKey(key);
let keySerialised;
if (format === 'raw') {
const arnEncoded = Buffer.from(key.arn);
keySerialised = (0, buffer_1.bufferToArrayBuffer)(arnEncoded);
}
else if (format === 'spki') {
keySerialised = await this.retrievePublicKey(key);
}
else {
throw new KmsError_1.KmsError(`Private key cannot be exported as ${format}`);
}
return keySerialised;
}
async onImportKey(format, keyData, algorithm) {
if (format !== 'raw') {
throw new KmsError_1.KmsError('Private key can only be exported to raw format');
}
const keyArn = Buffer.from(keyData).toString();
return new AwsKmsRsaPssPrivateKey_1.AwsKmsRsaPssPrivateKey(keyArn, algorithm.hash.name, this);
}
async onSign(_algorithm, key, data) {
requireAwsKmsKey(key);
const hashingAlgorithm = key.algorithm.hash.name;
const digest = await (0, crypto_1.hash)(data, hashingAlgorithm);
const awsHashAlgo = `RSASSA_PSS_${hashingAlgorithm.replace('-', '_')}`;
const command = new client_kms_1.SignCommand({
KeyId: key.arn,
Message: Buffer.from(digest),
MessageType: 'DIGEST',
SigningAlgorithm: awsHashAlgo,
});
const output = await this.client.send(command, REQUEST_OPTIONS);
return (0, buffer_1.bufferToArrayBuffer)(output.Signature);
}
async onVerify() {
throw new KmsError_1.KmsError('Signature verification is unsupported');
}
async destroyKey(key) {
requireAwsKmsKey(key);
const command = new client_kms_1.ScheduleKeyDeletionCommand({ KeyId: key.arn });
await this.client.send(command, REQUEST_OPTIONS);
}
async close() {
this.client.destroy();
}
async retrievePublicKey(key) {
const command = new client_kms_1.GetPublicKeyCommand({ KeyId: key.arn });
const response = await this.client.send(command, REQUEST_OPTIONS);
return (0, buffer_1.bufferToArrayBuffer)(response.PublicKey);
}
}
exports.AwsKmsRsaPssProvider = AwsKmsRsaPssProvider;
function requireAwsKmsKey(key) {
if (!(key instanceof AwsKmsRsaPssPrivateKey_1.AwsKmsRsaPssPrivateKey)) {
throw new KmsError_1.KmsError(`Only AWS KMS keys are supported (got ${key.constructor.name})`);
}
}
//# sourceMappingURL=AwsKmsRsaPssProvider.js.map