@planetarium/account-aws-kms
Version:
Libplanet account implementation using AWS KMS
290 lines (282 loc) • 10.4 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
AwsKmsAccount: () => AwsKmsAccount,
AwsKmsKeyStore: () => AwsKmsKeyStore,
KMSClient: () => import_client_kms3.KMSClient
});
module.exports = __toCommonJS(src_exports);
// src/AwsKmsAccount.ts
var import_client_kms = require("@aws-sdk/client-kms");
var import_secp256k1 = require("@noble/secp256k1");
// src/crypto/node.ts
var nc = __toESM(require("node:crypto"), 1);
var crypto = nc && typeof nc === "object" && "webcrypto" in nc ? nc.webcrypto : void 0;
// src/AwsKmsAccount.ts
var import_account = require("@planetarium/account");
var AwsKmsAccount = class {
#client;
keyId;
// TODO: This attribute is deprecated. We should remove it and make
// getPublicKey() method the only choice in the future.
/**
* @deprecated Use {@link getPublicKey()} instead.
*/
publicKey;
constructor(keyId, publicKey, client) {
this.keyId = keyId;
this.publicKey = publicKey;
this.#client = client;
}
getAddress() {
return Promise.resolve(import_account.Address.deriveFrom(this.publicKey));
}
getPublicKey() {
return Promise.resolve(this.publicKey);
}
async sign(message) {
const digest = await crypto.subtle.digest("SHA-256", message);
const digestArray = new Uint8Array(digest);
const cmd = new import_client_kms.SignCommand({
KeyId: this.keyId,
Message: digestArray,
MessageType: "DIGEST",
SigningAlgorithm: "ECDSA_SHA_256"
});
const response = await this.#client.send(cmd);
if (response.Signature == null)
throw new Error("Failed to sign message");
const sig = import_secp256k1.Signature.fromDER(response.Signature).normalizeS();
return import_account.Signature.fromHex(sig.toDERHex());
}
};
__name(AwsKmsAccount, "AwsKmsAccount");
// src/asn1.ts
var import_asn1js = require("asn1js");
var SubjectPublicKeyInfo = new import_asn1js.Sequence({
name: "subjectPublicKeyInfo",
value: [
new import_asn1js.Sequence({
name: "algorithm",
value: [
new import_asn1js.ObjectIdentifier({
name: "algorithm"
}),
new import_asn1js.Any({
name: "parameters",
optional: true
})
]
}),
new import_asn1js.BitString({
name: "subjectPublicKey"
})
]
});
function parseSubjectPublicKeyInfo(buf) {
const { result, verified } = (0, import_asn1js.verifySchema)(buf, SubjectPublicKeyInfo);
if (!verified) {
throw new RangeError("Failed to verify SubjectPublicKeyInfo data");
}
const bitstring = result.valueBlock.value[1];
return bitstring.valueBlock.valueHexView;
}
__name(parseSubjectPublicKeyInfo, "parseSubjectPublicKeyInfo");
// src/AwsKmsKeyStore.ts
var import_client_kms2 = require("@aws-sdk/client-kms");
var import_account2 = require("@planetarium/account");
var AwsKmsKeyStore = class {
#client;
#options;
constructor(client, options = {}) {
this.#client = client;
this.#options = {
listWindow: options.listWindow ?? 100,
scopingTags: options.scopingTags ?? {}
};
}
#isValidKey(metadata) {
return metadata.KeyId != null && metadata.Enabled === true && metadata.DeletionDate == null && metadata.KeySpec === "ECC_SECG_P256K1" && metadata.KeyUsage === "SIGN_VERIFY";
}
#mapMetadata(metadata) {
return {
customKeyStoreId: metadata.CustomKeyStoreId,
description: metadata.Description ?? "",
multiRegion: metadata.MultiRegion ?? false,
origin: metadata.Origin ?? import_client_kms2.OriginType.AWS_KMS
};
}
async #hasTags(keyId, tags) {
let marker;
const remainTags = new Map(Object.entries(tags));
do {
const cmd = new import_client_kms2.ListResourceTagsCommand({ KeyId: keyId, Marker: marker });
const result = await this.#client.send(cmd);
marker = result.NextMarker;
if (result.Tags == null)
continue;
for (const { TagKey, TagValue } of result.Tags) {
if (TagKey == null || TagValue == null)
continue;
if (remainTags.has(TagKey)) {
if (remainTags.get(TagKey) === TagValue) {
remainTags.delete(TagKey);
if (remainTags.size < 1)
return true;
} else
return false;
}
}
} while (marker != null);
return remainTags.size < 1;
}
async *list() {
let nextMarker;
const hasScopingTags = Object.keys(this.#options).length > 0;
do {
const listCmd = new import_client_kms2.ListKeysCommand({
Marker: nextMarker,
Limit: this.#options.listWindow
});
const resp = await this.#client.send(listCmd);
const keys = resp.Keys ?? [];
for (let i = 0; i < keys.length; i += 5) {
const promises = keys.slice(i, i + 5).map(({ KeyId }) => new import_client_kms2.DescribeKeyCommand({ KeyId })).map((cmd) => this.#client.send(cmd));
const responses = await Promise.all(promises);
for (const resp2 of responses) {
const metadata = resp2.KeyMetadata;
if (metadata == null || !this.#isValidKey(metadata) || hasScopingTags && !this.#hasTags(metadata.KeyId, this.#options.scopingTags)) {
continue;
}
yield {
keyId: metadata.KeyId,
metadata: this.#mapMetadata(metadata),
createdAt: metadata.CreationDate
};
}
}
nextMarker = resp.NextMarker;
} while (nextMarker != null);
}
async get(keyId) {
const descCmd = new import_client_kms2.DescribeKeyCommand({ KeyId: keyId });
const pubKeyCmd = new import_client_kms2.GetPublicKeyCommand({ KeyId: keyId });
const descPromise = this.#client.send(descCmd);
const pubKeyPromise = this.#client.send(pubKeyCmd);
let descResp;
let pubKeyResp;
try {
[descResp, pubKeyResp] = await Promise.all([descPromise, pubKeyPromise]);
} catch (e) {
if (e instanceof import_client_kms2.NotFoundException || e instanceof import_client_kms2.KMSInvalidStateException) {
return { result: "keyNotFound", keyId };
}
return { result: "error", keyId, message: `${e}` };
}
if (descResp.KeyMetadata == null || !this.#isValidKey(descResp.KeyMetadata) || pubKeyResp.PublicKey == null) {
return { result: "keyNotFound", keyId };
}
const publicKeyBytes = parseSubjectPublicKeyInfo(
pubKeyResp.PublicKey
);
const publicKey = import_account2.PublicKey.fromBytes(publicKeyBytes, "uncompressed");
return {
result: "success",
keyId,
account: new AwsKmsAccount(keyId, publicKey, this.#client),
metadata: this.#mapMetadata(descResp.KeyMetadata),
createdAt: descResp.KeyMetadata.CreationDate
};
}
async generate(metadata) {
const cmd = new import_client_kms2.CreateKeyCommand({
KeySpec: "ECC_SECG_P256K1",
KeyUsage: "SIGN_VERIFY",
CustomKeyStoreId: metadata?.customKeyStoreId,
Description: metadata?.description,
MultiRegion: metadata?.multiRegion,
Origin: metadata?.origin,
Tags: Object.entries(this.#options.scopingTags).map(
([TagKey, TagValue]) => ({ TagKey, TagValue })
)
});
let response;
try {
response = await this.#client.send(cmd);
} catch (e) {
return { result: "error", message: `${e}` };
}
const keyId = response.KeyMetadata?.KeyId;
if (keyId == null) {
return { result: "error", message: "failed to determine keyId" };
}
const pubKeyCmd = new import_client_kms2.GetPublicKeyCommand({ KeyId: keyId });
let pubKeyResp;
try {
pubKeyResp = await this.#client.send(pubKeyCmd);
} catch (e) {
return { result: "error", message: `${e}` };
}
if (pubKeyResp.PublicKey == null) {
return { result: "error", message: "failed to get public key" };
}
const publicKeyBytes = parseSubjectPublicKeyInfo(
pubKeyResp.PublicKey
);
const publicKey = import_account2.PublicKey.fromBytes(publicKeyBytes, "uncompressed");
const account = new AwsKmsAccount(keyId, publicKey, this.#client);
return { result: "success", keyId, account };
}
async delete(keyId) {
const cmd = new import_client_kms2.ScheduleKeyDeletionCommand({ KeyId: keyId });
try {
await this.#client.send(cmd);
} catch (e) {
if (e instanceof import_client_kms2.NotFoundException || e instanceof import_client_kms2.KMSInvalidStateException) {
return { result: "keyNotFound", keyId };
}
return { result: "error", message: `${e}` };
}
return { result: "success", keyId };
}
};
__name(AwsKmsKeyStore, "AwsKmsKeyStore");
// src/index.ts
var import_client_kms3 = require("@aws-sdk/client-kms");
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
AwsKmsAccount,
AwsKmsKeyStore,
KMSClient
});
/*! For license information please see index.cjs.LEGAL.txt */
//# sourceMappingURL=index.cjs.map