@dwn-protocol/id-sdk
Version:
SDK for accessing the features and capabilities
615 lines • 26.6 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PrivateKeyStoreMemory = exports.PrivateKeyStoreDwn = exports.KeyStoreMemory = exports.KeyStoreDwn = void 0;
const cryptoUtils = __importStar(require("../crypto/utils.js"));
const index_js_1 = require("../common/index.js");
const utils_js_1 = require("./utils.js");
/**
* An implementation of `ManagedKeyStore` that stores key metadata and
* public key material in a DWN.
*
* An instance of this class can be used by `KeyManager` or
* an implementation of `KeyManagementSystem`.
*/
class KeyStoreDwn {
constructor(options) {
this._keyRecordProperties = {
dataFormat: 'application/json',
schema: 'https://identity.foundation/schemas/dwn/managed-key'
};
const { schema } = options !== null && options !== void 0 ? options : {};
if (schema) {
this._keyRecordProperties.schema = schema;
}
}
async deleteKey(options) {
var _a;
const { agent, context, id } = options;
// Determine which DID to use to author DWN messages.
const authorDid = await this.getAuthor({ agent, context });
// Query the DWN for all stored key objects.
const { reply: queryReply } = await this.getKeyRecords(agent, context);
// Loop through all of the entries and try to find a match.
let matchingRecordId;
for (const record of (_a = queryReply.entries) !== null && _a !== void 0 ? _a : []) {
if (record.encodedData) {
const storedKey = this.decodeKey(record.encodedData);
const storedKeyId = (0, utils_js_1.isManagedKeyPair)(storedKey) ? storedKey.publicKey.id : storedKey.id;
if (storedKey && storedKeyId === id) {
matchingRecordId = record.recordId;
break;
}
}
}
// Return undefined if the specified key was not found in the store.
if (!matchingRecordId)
return false;
// If a record for the specified key was found, attempt to delete it.
const { reply: { status } } = await agent.dwnManager.processRequest({
author: authorDid,
target: authorDid,
messageType: 'RecordsDelete',
messageOptions: {
recordId: matchingRecordId
}
});
// If the key was successfully deleted, return true;
if (status.code === 202)
return true;
// If the key could not be deleted, return false;
return false;
}
async findKey(options) {
var _a;
const { agent, alias, context, id } = options;
// Query the DWN for all stored managed key objects.
const { reply: queryReply } = await this.getKeyRecords(agent, context);
// Loop through all of the entries and return a match, if found.
for (const record of (_a = queryReply.entries) !== null && _a !== void 0 ? _a : []) {
if (record.encodedData) {
const storedKey = this.decodeKey(record.encodedData);
if ((0, utils_js_1.isManagedKeyPair)(storedKey)) {
if (storedKey.publicKey.id === id)
return storedKey;
if (storedKey.publicKey.alias === alias)
return storedKey;
}
else {
if (storedKey.id === id)
return storedKey;
if (storedKey.alias === alias)
return storedKey;
}
}
}
// Return undefined if no matches were found.
return undefined;
}
async getKey(options) {
var _a;
const { agent, context, id } = options;
// Query the DWN for all stored managed key objects.
const { reply: queryReply } = await this.getKeyRecords(agent, context);
// Loop through all of the entries and return a match, if found.
for (const record of (_a = queryReply.entries) !== null && _a !== void 0 ? _a : []) {
if (record.encodedData) {
const storedKey = this.decodeKey(record.encodedData);
const storedKeyId = (0, utils_js_1.isManagedKeyPair)(storedKey) ? storedKey.publicKey.id : storedKey.id;
if (storedKeyId === id)
return storedKey;
}
}
// Return undefined if no matches were found.
return undefined;
}
async importKey(options) {
const { agent, context, key } = options;
let keyId;
if ((0, utils_js_1.isManagedKeyPair)(key)) {
keyId = key.publicKey.id;
}
else {
// If an ID wasn't specified, generate one.
if (!key.id) {
key.id = cryptoUtils.randomUuid();
}
keyId = key.id;
}
// Determine which DID to use to author DWN messages.
const authorDid = await this.getAuthor({ agent, context });
// Check if the key being imported is already present in the store.
const duplicateFound = await this.getKey({ agent, context, id: keyId });
if (duplicateFound) {
throw new Error(`KeyStoreDwn: Key with ID already exists: '${keyId}'`);
}
// Encode the managed key or key pair as bytes.
const encodedKey = this.encodeKey(key);
const { reply: { status } } = await agent.dwnManager.processRequest({
author: authorDid,
target: authorDid,
messageType: 'RecordsWrite',
messageOptions: Object.assign({}, this._keyRecordProperties),
dataStream: new Blob([encodedKey])
});
// If the write fails, throw an error.
if (status.code !== 202) {
throw new Error('DidStoreDwn: Failed to write imported DID to store.');
}
return keyId;
}
async listKeys(options) {
var _a;
const { agent, context } = options;
// Query the DWN for all stored managed key objects.
const { reply: queryReply } = await this.getKeyRecords(agent, context);
// Loop through all of the entries and accumulate the key objects.
let storedKeys = [];
for (const record of (_a = queryReply.entries) !== null && _a !== void 0 ? _a : []) {
if (record.encodedData) {
const storedKey = this.decodeKey(record.encodedData);
storedKeys.push(storedKey);
}
}
return storedKeys;
}
async updateKey(options) {
var _a;
const { agent, context, id } = options;
const propertyUpdates = { alias: options.alias, metadata: options.metadata };
// Determine which DID to use to author DWN messages.
const authorDid = await this.getAuthor({ agent, context });
// Query the DWN for all stored managed key objects.
const { reply: queryReply } = await this.getKeyRecords(agent, context);
// Confirm the key being updated is already present in the store.
let keyToUpdate;
let recordToUpdate;
for (const entry of (_a = queryReply.entries) !== null && _a !== void 0 ? _a : []) {
const { encodedData } = entry, record = __rest(entry, ["encodedData"]);
if (encodedData) {
const storedKey = this.decodeKey(encodedData);
const storedKeyId = (0, utils_js_1.isManagedKeyPair)(storedKey) ? storedKey.publicKey.id : storedKey.id;
if (storedKey && storedKeyId === id) {
keyToUpdate = storedKey;
recordToUpdate = record;
break;
}
}
}
// Key with given ID not present so update operation cannot proceed.
if (!recordToUpdate || !keyToUpdate)
return false;
// Make a deep copy of the update properties to ensure all nested objects do not share references.
(0, index_js_1.removeUndefinedProperties)(propertyUpdates);
(0, index_js_1.removeEmptyObjects)(propertyUpdates);
const clonedUpdates = structuredClone(propertyUpdates);
// Update the given properties of the key.
if ((0, utils_js_1.isManagedKeyPair)(keyToUpdate)) {
keyToUpdate.privateKey = Object.assign(Object.assign({}, keyToUpdate.privateKey), clonedUpdates);
keyToUpdate.publicKey = Object.assign(Object.assign({}, keyToUpdate.publicKey), clonedUpdates);
}
else {
keyToUpdate = Object.assign(Object.assign({}, keyToUpdate), clonedUpdates);
}
// Encode the updated key or key pair as bytes.
const updatedKeyBytes = this.encodeKey(keyToUpdate);
// Assemble the update messsage, including record ID and context ID, if any.
let messageOptions = Object.assign({}, recordToUpdate.descriptor);
messageOptions.contextId = recordToUpdate.contextId;
messageOptions.recordId = recordToUpdate.recordId;
/** Remove properties from the update messageOptions to let the DWN SDK
* auto-fill. Otherwisse, you will get 409 Conflict errors. */
delete messageOptions.dataCid;
delete messageOptions.dataSize;
delete messageOptions.data;
delete messageOptions.messageTimestamp;
// Overwrite the entry in the store with the updated object.
const { reply: { status } } = await agent.dwnManager.processRequest({
author: authorDid,
target: authorDid,
messageType: 'RecordsWrite',
messageOptions,
dataStream: new Blob([updatedKeyBytes])
});
// If the write fails, throw an error.
if (status.code !== 202) {
throw new Error('DidStoreDwn: Failed to write updated key to store.');
}
return true;
}
decodeKey(keyEncodedData) {
const encodedKey = index_js_1.Convert.base64Url(keyEncodedData).toObject();
if ('publicKey' in encodedKey) {
const privateKeyMaterial = encodedKey.privateKey.material
? index_js_1.Convert.base64Url(encodedKey.privateKey.material).toUint8Array()
: undefined;
const publicKeyMaterial = encodedKey.publicKey.material
? index_js_1.Convert.base64Url(encodedKey.publicKey.material).toUint8Array()
: undefined;
const managedKeyPair = {
privateKey: Object.assign(Object.assign({}, encodedKey.privateKey), { material: privateKeyMaterial }),
publicKey: Object.assign(Object.assign({}, encodedKey.publicKey), { material: publicKeyMaterial })
};
return managedKeyPair;
}
else {
const material = encodedKey.material
? index_js_1.Convert.base64Url(encodedKey.material).toUint8Array()
: undefined;
const managedKey = Object.assign(Object.assign({}, encodedKey), { material });
return managedKey;
}
}
encodeKey(managedKey) {
let encodedKey;
if ((0, utils_js_1.isManagedKeyPair)(managedKey)) {
const privateKeyMaterial = managedKey.privateKey.material
? index_js_1.Convert.uint8Array(managedKey.privateKey.material).toBase64Url()
: undefined;
const publicKeyMaterial = managedKey.publicKey.material
? index_js_1.Convert.uint8Array(managedKey.publicKey.material).toBase64Url()
: undefined;
encodedKey = {
privateKey: Object.assign(Object.assign({}, managedKey.privateKey), { material: privateKeyMaterial }),
publicKey: Object.assign(Object.assign({}, managedKey.publicKey), { material: publicKeyMaterial })
};
}
else {
const material = managedKey.material
? index_js_1.Convert.uint8Array(managedKey.material).toBase64Url()
: undefined;
encodedKey = Object.assign(Object.assign({}, managedKey), { material });
}
const keyBytes = index_js_1.Convert.object(encodedKey).toUint8Array();
return keyBytes;
}
async getAuthor(options) {
const { agent, context } = options;
// If `context` is specified, DWN messages will be signed by this DID.
if (context)
return context;
// If Agent has an agentDid, use it to sign DWN messages.
if (agent.agentDid)
return agent.agentDid;
// If `context` and `agent.agentDid`are undefined, throw error.
throw new Error(`KeyStoreDwn: Agent property 'agentDid' is undefined and no context was specified.`);
}
async getKeyRecords(agent, context) {
// Determine which DID to use to author DWN messages.
const authorDid = await this.getAuthor({ agent, context });
const dwnResponse = await agent.dwnManager.processRequest({
author: authorDid,
target: authorDid,
messageType: 'RecordsQuery',
messageOptions: {
filter: Object.assign({}, this._keyRecordProperties)
}
});
return dwnResponse;
}
}
exports.KeyStoreDwn = KeyStoreDwn;
/**
* An implementation of `ManagedKeyStore` that stores key metadata and
* public key material in memory.
*
* An instance of this class can be used by `KeyManager` or
* an implementation of `KeyManagementSystem`.
*/
class KeyStoreMemory {
constructor() {
/**
* A private field that contains the Map used as the in-memory key-value store.
*/
this.store = new Map();
}
async deleteKey({ id }) {
if (this.store.has(id)) {
// Key with given ID exists so proceed with delete.
this.store.delete(id);
return true;
}
// Key with given ID not present so delete operation not possible.
return false;
}
async findKey(options) {
let { alias, id } = options;
// Get key by ID.
if (id)
return this.store.get(id);
if (alias) {
// Search through the store to find a matching entry.
for (const key of await this.listKeys()) {
if ('alias' in key && key.alias === alias)
return key;
if ('publicKey' in key && key.publicKey.alias === alias)
return key;
}
}
return undefined;
}
async getKey({ id }) {
return this.store.get(id);
}
async importKey({ key }) {
let id;
if ((0, utils_js_1.isManagedKeyPair)(key)) {
id = key.publicKey.id;
}
else {
// If an ID wasn't specified, generate one.
if (!key.id) {
key.id = cryptoUtils.randomUuid();
}
id = key.id;
}
if (this.store.has(id)) {
// Key with given ID already exists so import operation cannot proceed.
throw new Error(`KeyStoreMemory: Key with ID already exists: '${id}'`);
}
// Make a deep copy of the key so that the object stored does not share the same references as the input key.
const clonedKey = structuredClone(key);
this.store.set(id, clonedKey);
return id;
}
async listKeys() {
return Array.from(this.store.values());
}
async updateKey(options) {
const id = options.id;
const propertyUpdates = { alias: options.alias, metadata: options.metadata };
const keyExists = this.store.has(id);
if (!keyExists) {
// Key with given ID not present so update operation cannot proceed.
return false;
}
// Retrieve the current value of the key from the store.
let key = await this.getKey({ id });
// Make a deep copy of the update properties to ensure all nested objects do not share references.
(0, index_js_1.removeUndefinedProperties)(propertyUpdates);
(0, index_js_1.removeEmptyObjects)(propertyUpdates);
const clonedUpdates = structuredClone(propertyUpdates);
// Update the given properties of the key.
if ((0, utils_js_1.isManagedKeyPair)(key)) {
key.privateKey = Object.assign(Object.assign({}, key.privateKey), clonedUpdates);
key.publicKey = Object.assign(Object.assign({}, key.publicKey), clonedUpdates);
}
else {
key = Object.assign(Object.assign(Object.assign({}, key), clonedUpdates), { id: key.id });
}
// Overwrite the entry in the store with the updated object.
this.store.set(id, key);
return true;
}
}
exports.KeyStoreMemory = KeyStoreMemory;
/**
* An implementation of `ManagedKeyStore` that stores private key
* material in a DWN.
*
* An instance of this class can be used by an implementation of
* `KeyManagementSystem`.
*/
class PrivateKeyStoreDwn {
constructor() {
this._keyRecordProperties = {
dataFormat: 'application/json',
schema: 'https://identity.foundation/schemas/dwn/kms-private-key'
};
}
async deleteKey(options) {
var _a;
const { agent, context, id } = options;
// Determine which DID to use to author DWN messages.
const authorDid = await this.getAuthor({ agent, context });
// Query the DWN for all stored key objects.
const { reply: queryReply } = await this.getKeyRecords(agent, context);
// Loop through all of the entries and try to find a match.
let matchingRecordId;
for (const record of (_a = queryReply.entries) !== null && _a !== void 0 ? _a : []) {
if (record.encodedData) {
const storedKey = this.decodeKey(record.encodedData);
if (storedKey && storedKey.id === id) {
matchingRecordId = record.recordId;
break;
}
}
}
// Return undefined if the specified key was not found in the store.
if (!matchingRecordId)
return false;
// If a record for the specified key was found, attempt to delete it.
const { reply: { status } } = await agent.dwnManager.processRequest({
author: authorDid,
target: authorDid,
messageType: 'RecordsDelete',
messageOptions: {
recordId: matchingRecordId
}
});
// If the key was successfully deleted, return true;
if (status.code === 202)
return true;
// If the key could not be deleted, return false;
return false;
}
async findKey() {
throw new Error(`PrivateKeyStoreDwn: Method not implemented: 'findKey'`);
}
async getKey(options) {
var _a;
const { agent, context, id } = options;
// Query the DWN for all stored key objects.
const { reply: queryReply } = await this.getKeyRecords(agent, context);
// Loop through all of the entries and return a match, if found.
for (const record of (_a = queryReply.entries) !== null && _a !== void 0 ? _a : []) {
if (record.encodedData) {
const storedKey = this.decodeKey(record.encodedData);
if (storedKey.id === id)
return storedKey;
}
}
// Return undefined if no matches were found.
return undefined;
}
async importKey(options) {
const { agent, context, key } = options;
if (!key.material)
throw new TypeError(`Required parameter missing: 'material'`);
if (!key.type)
throw new TypeError(`Required parameter missing: 'type'`);
// Determine which DID to use to author DWN messages.
const authorDid = await this.getAuthor({ agent, context });
// Encode the managed key or key pair as bytes.
const id = cryptoUtils.randomUuid(); // Generate a random ID.
const encodedPrivateKey = this.encodeKey(Object.assign(Object.assign({}, key), { id }));
const { reply: { status } } = await agent.dwnManager.processRequest({
author: authorDid,
target: authorDid,
messageType: 'RecordsWrite',
messageOptions: Object.assign({}, this._keyRecordProperties),
dataStream: new Blob([encodedPrivateKey])
});
// If the write fails, throw an error.
if (status.code !== 202) {
throw new Error('PrivateKeyStoreDwn: Failed to write imported DID to store.');
}
return id;
}
async listKeys(options) {
var _a;
const { agent, context } = options;
// Query the DWN for all stored key objects.
const { reply: queryReply } = await this.getKeyRecords(agent, context);
// Loop through all of the entries and accumulate the key objects.
let storedKeys = [];
for (const record of (_a = queryReply.entries) !== null && _a !== void 0 ? _a : []) {
if (record.encodedData) {
const storedKey = this.decodeKey(record.encodedData);
storedKeys.push(storedKey);
}
}
return storedKeys;
}
async updateKey() {
throw new Error(`PrivateKeyStoreMemory: Method not implemented: 'updateKey'`);
}
decodeKey(keyEncodedData) {
const encodedKey = index_js_1.Convert.base64Url(keyEncodedData).toObject();
const privateKey = Object.assign(Object.assign({}, encodedKey), { material: index_js_1.Convert.base64Url(encodedKey.material).toUint8Array() });
return privateKey;
}
encodeKey(privateKey) {
const encodedKey = Object.assign(Object.assign({}, privateKey), { material: index_js_1.Convert.uint8Array(privateKey.material).toBase64Url() });
const keyBytes = index_js_1.Convert.object(encodedKey).toUint8Array();
return keyBytes;
}
async getAuthor(options) {
const { agent, context } = options;
// If `context` is specified, DWN messages will be signed by this DID.
if (context)
return context;
// If Agent has an agentDid, use it to sign DWN messages.
if (agent.agentDid)
return agent.agentDid;
// If `context` and `agent.agentDid`are undefined, throw error.
throw new Error(`PrivateKeyStoreDwn: Agent property 'agentDid' is undefined and no context was specified.`);
}
async getKeyRecords(agent, context) {
// Determine which DID to use to author DWN messages.
const authorDid = await this.getAuthor({ agent, context });
const dwnResponse = await agent.dwnManager.processRequest({
author: authorDid,
target: authorDid,
messageType: 'RecordsQuery',
messageOptions: {
filter: Object.assign({}, this._keyRecordProperties)
}
});
return dwnResponse;
}
}
exports.PrivateKeyStoreDwn = PrivateKeyStoreDwn;
/**
* An implementation of `ManagedKeyStore` that stores private key
* material in memory.
*
* An instance of this class can be used by an implementation of
* `KeyManagementSystem`.
*/
class PrivateKeyStoreMemory {
constructor() {
/**
* A private field that contains the Map used as the in-memory key-value store.
*/
this.store = new Map();
}
async deleteKey({ id }) {
if (this.store.has(id)) {
// Key with given ID exists so proceed with delete.
this.store.delete(id);
return true;
}
// Key with given ID not present so delete operation not possible.
return false;
}
async findKey() {
throw new Error(`PrivateKeyStoreMemory: Method not implemented: 'findKey'`);
}
async getKey({ id }) {
return this.store.get(id);
}
async importKey({ key }) {
if (!key.material)
throw new TypeError(`Required parameter missing: 'material'`);
if (!key.type)
throw new TypeError(`Required parameter missing: 'type'`);
// Make a deep copy of the key so that the object stored does not share the same references as the input key.
// The private key material is transferred to the new object, making the original obj.material unusable.
const clonedKey = structuredClone(key, { transfer: [key.material.buffer] });
clonedKey.id = cryptoUtils.randomUuid();
this.store.set(clonedKey.id, clonedKey);
return clonedKey.id;
}
async listKeys() {
return Array.from(this.store.values());
}
async updateKey() {
throw new Error(`PrivateKeyStoreMemory: Method not implemented: 'updateKey'`);
}
}
exports.PrivateKeyStoreMemory = PrivateKeyStoreMemory;
//# sourceMappingURL=store-managed-key.js.map