- Add GETTING_STARTED.md with quick start guide and development modes - Add INSTALL.sh automated installation script - Add INSTALLATION_CHECKLIST.md, INSTALLATION_SUCCESS.md, and INSTALLATION_SUMMARY.md - Add QUICK_REFERENCE.md for common commands - Add SETUP_GUIDE.md with detailed setup instructions - Update README.md with improved project overview - Add did-wallet app dependencies and node_modules
271 lines
13 KiB
JavaScript
271 lines
13 KiB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
});
|
|
};
|
|
import * as URI from 'uri-js';
|
|
import ErrorCode from './ErrorCode.js';
|
|
import InputValidator from './InputValidator.js';
|
|
import IonError from './IonError.js';
|
|
import IonSdkConfig from './IonSdkConfig.js';
|
|
import JsonCanonicalizer from './JsonCanonicalizer.js';
|
|
import Multihash from './Multihash.js';
|
|
import OperationKeyType from './enums/OperationKeyType.js';
|
|
import OperationType from './enums/OperationType.js';
|
|
import PatchAction from './enums/PatchAction.js';
|
|
/**
|
|
* Class containing operations related to ION requests.
|
|
*/
|
|
export default class IonRequest {
|
|
/**
|
|
* Creates an ION DID create request.
|
|
* @param input.document The initial state to be associate with the ION DID to be created using a `replace` document patch action.
|
|
*/
|
|
static createCreateRequest(input) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
const recoveryKey = input.recoveryKey;
|
|
const updateKey = input.updateKey;
|
|
const didDocumentKeys = input.document.publicKeys;
|
|
const services = input.document.services;
|
|
// Validate recovery and update public keys.
|
|
InputValidator.validateEs256kOperationKey(recoveryKey, OperationKeyType.Public);
|
|
InputValidator.validateEs256kOperationKey(updateKey, OperationKeyType.Public);
|
|
// Validate all given DID Document keys.
|
|
IonRequest.validateDidDocumentKeys(didDocumentKeys);
|
|
// Validate all given service.
|
|
IonRequest.validateServices(services);
|
|
const hashAlgorithmInMultihashCode = IonSdkConfig.hashAlgorithmInMultihashCode;
|
|
const patches = [{
|
|
action: PatchAction.Replace,
|
|
document: input.document
|
|
}];
|
|
const delta = {
|
|
updateCommitment: yield Multihash.canonicalizeThenDoubleHashThenEncode(updateKey, hashAlgorithmInMultihashCode),
|
|
patches
|
|
};
|
|
IonRequest.validateDeltaSize(delta);
|
|
const deltaHash = yield Multihash.canonicalizeThenHashThenEncode(delta, hashAlgorithmInMultihashCode);
|
|
const suffixData = {
|
|
deltaHash,
|
|
recoveryCommitment: yield Multihash.canonicalizeThenDoubleHashThenEncode(recoveryKey, hashAlgorithmInMultihashCode)
|
|
};
|
|
const operationRequest = {
|
|
type: OperationType.Create,
|
|
suffixData: suffixData,
|
|
delta: delta
|
|
};
|
|
return operationRequest;
|
|
});
|
|
}
|
|
static createDeactivateRequest(input) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
// Validate DID suffix
|
|
IonRequest.validateDidSuffix(input.didSuffix);
|
|
// Validates recovery public key
|
|
InputValidator.validateEs256kOperationKey(input.recoveryPublicKey, OperationKeyType.Public);
|
|
const hashAlgorithmInMultihashCode = IonSdkConfig.hashAlgorithmInMultihashCode;
|
|
const revealValue = yield Multihash.canonicalizeThenHashThenEncode(input.recoveryPublicKey, hashAlgorithmInMultihashCode);
|
|
const dataToBeSigned = {
|
|
didSuffix: input.didSuffix,
|
|
recoveryKey: input.recoveryPublicKey
|
|
};
|
|
const compactJws = yield input.signer.sign({ alg: 'ES256K' }, dataToBeSigned);
|
|
return {
|
|
type: OperationType.Deactivate,
|
|
didSuffix: input.didSuffix,
|
|
revealValue: revealValue,
|
|
signedData: compactJws
|
|
};
|
|
});
|
|
}
|
|
static createRecoverRequest(input) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
// Validate DID suffix
|
|
IonRequest.validateDidSuffix(input.didSuffix);
|
|
// Validate recovery public key
|
|
InputValidator.validateEs256kOperationKey(input.recoveryPublicKey, OperationKeyType.Public);
|
|
// Validate next recovery public key
|
|
InputValidator.validateEs256kOperationKey(input.nextRecoveryPublicKey, OperationKeyType.Public);
|
|
// Validate next update public key
|
|
InputValidator.validateEs256kOperationKey(input.nextUpdatePublicKey, OperationKeyType.Public);
|
|
// Validate all given DID Document keys.
|
|
IonRequest.validateDidDocumentKeys(input.document.publicKeys);
|
|
// Validate all given service.
|
|
IonRequest.validateServices(input.document.services);
|
|
const hashAlgorithmInMultihashCode = IonSdkConfig.hashAlgorithmInMultihashCode;
|
|
const revealValue = yield Multihash.canonicalizeThenHashThenEncode(input.recoveryPublicKey, hashAlgorithmInMultihashCode);
|
|
const patches = [{
|
|
action: PatchAction.Replace,
|
|
document: input.document
|
|
}];
|
|
const nextUpdateCommitmentHash = yield Multihash.canonicalizeThenDoubleHashThenEncode(input.nextUpdatePublicKey, hashAlgorithmInMultihashCode);
|
|
const delta = {
|
|
patches,
|
|
updateCommitment: nextUpdateCommitmentHash
|
|
};
|
|
const deltaHash = yield Multihash.canonicalizeThenHashThenEncode(delta, hashAlgorithmInMultihashCode);
|
|
const nextRecoveryCommitmentHash = yield Multihash.canonicalizeThenDoubleHashThenEncode(input.nextRecoveryPublicKey, hashAlgorithmInMultihashCode);
|
|
const dataToBeSigned = {
|
|
recoveryCommitment: nextRecoveryCommitmentHash,
|
|
recoveryKey: input.recoveryPublicKey,
|
|
deltaHash: deltaHash
|
|
};
|
|
const compactJws = yield input.signer.sign({ alg: 'ES256K' }, dataToBeSigned);
|
|
return {
|
|
type: OperationType.Recover,
|
|
didSuffix: input.didSuffix,
|
|
revealValue: revealValue,
|
|
delta: delta,
|
|
signedData: compactJws
|
|
};
|
|
});
|
|
}
|
|
static createUpdateRequest(input) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
// Validate DID suffix
|
|
IonRequest.validateDidSuffix(input.didSuffix);
|
|
// Validate update public key
|
|
InputValidator.validateEs256kOperationKey(input.updatePublicKey, OperationKeyType.Public);
|
|
// Validate next update public key
|
|
InputValidator.validateEs256kOperationKey(input.nextUpdatePublicKey, OperationKeyType.Public);
|
|
// Validate all given service.
|
|
IonRequest.validateServices(input.servicesToAdd);
|
|
// Validate all given DID Document keys.
|
|
IonRequest.validateDidDocumentKeys(input.publicKeysToAdd);
|
|
// Validate all given service id to remove.
|
|
if (input.idsOfServicesToRemove !== undefined) {
|
|
for (const id of input.idsOfServicesToRemove) {
|
|
InputValidator.validateId(id);
|
|
}
|
|
}
|
|
// Validate all given public key id to remove.
|
|
if (input.idsOfPublicKeysToRemove !== undefined) {
|
|
for (const id of input.idsOfPublicKeysToRemove) {
|
|
InputValidator.validateId(id);
|
|
}
|
|
}
|
|
const patches = [];
|
|
// Create patches for add services
|
|
const servicesToAdd = input.servicesToAdd;
|
|
if (servicesToAdd !== undefined && servicesToAdd.length > 0) {
|
|
const patch = {
|
|
action: PatchAction.AddServices,
|
|
services: servicesToAdd
|
|
};
|
|
patches.push(patch);
|
|
}
|
|
// Create patches for remove services
|
|
const idsOfServicesToRemove = input.idsOfServicesToRemove;
|
|
if (idsOfServicesToRemove !== undefined && idsOfServicesToRemove.length > 0) {
|
|
const patch = {
|
|
action: PatchAction.RemoveServices,
|
|
ids: idsOfServicesToRemove
|
|
};
|
|
patches.push(patch);
|
|
}
|
|
// Create patches for adding public keys
|
|
const publicKeysToAdd = input.publicKeysToAdd;
|
|
if (publicKeysToAdd !== undefined && publicKeysToAdd.length > 0) {
|
|
const patch = {
|
|
action: PatchAction.AddPublicKeys,
|
|
publicKeys: publicKeysToAdd
|
|
};
|
|
patches.push(patch);
|
|
}
|
|
// Create patch for removing public keys
|
|
const idsOfPublicKeysToRemove = input.idsOfPublicKeysToRemove;
|
|
if (idsOfPublicKeysToRemove !== undefined && idsOfPublicKeysToRemove.length > 0) {
|
|
const patch = {
|
|
action: PatchAction.RemovePublicKeys,
|
|
ids: idsOfPublicKeysToRemove
|
|
};
|
|
patches.push(patch);
|
|
}
|
|
const hashAlgorithmInMultihashCode = IonSdkConfig.hashAlgorithmInMultihashCode;
|
|
const revealValue = yield Multihash.canonicalizeThenHashThenEncode(input.updatePublicKey, hashAlgorithmInMultihashCode);
|
|
const nextUpdateCommitmentHash = yield Multihash.canonicalizeThenDoubleHashThenEncode(input.nextUpdatePublicKey, hashAlgorithmInMultihashCode);
|
|
const delta = {
|
|
patches,
|
|
updateCommitment: nextUpdateCommitmentHash
|
|
};
|
|
const deltaHash = yield Multihash.canonicalizeThenHashThenEncode(delta, hashAlgorithmInMultihashCode);
|
|
const dataToBeSigned = {
|
|
updateKey: input.updatePublicKey,
|
|
deltaHash
|
|
};
|
|
const compactJws = yield input.signer.sign({ alg: 'ES256K' }, dataToBeSigned);
|
|
return {
|
|
type: OperationType.Update,
|
|
didSuffix: input.didSuffix,
|
|
revealValue,
|
|
delta,
|
|
signedData: compactJws
|
|
};
|
|
});
|
|
}
|
|
static validateDidSuffix(didSuffix) {
|
|
Multihash.validateEncodedHashComputedUsingSupportedHashAlgorithm(didSuffix, 'didSuffix');
|
|
}
|
|
static validateDidDocumentKeys(publicKeys) {
|
|
if (publicKeys === undefined) {
|
|
return;
|
|
}
|
|
// Validate each public key.
|
|
const publicKeyIdSet = new Set();
|
|
for (const publicKey of publicKeys) {
|
|
if (Array.isArray(publicKey.publicKeyJwk)) {
|
|
throw new IonError(ErrorCode.DidDocumentPublicKeyMissingOrIncorrectType, `DID Document key 'publicKeyJwk' property is not a non-array object.`);
|
|
}
|
|
InputValidator.validateId(publicKey.id);
|
|
// 'id' must be unique across all given keys.
|
|
if (publicKeyIdSet.has(publicKey.id)) {
|
|
throw new IonError(ErrorCode.DidDocumentPublicKeyIdDuplicated, `DID Document key with ID '${publicKey.id}' already exists.`);
|
|
}
|
|
publicKeyIdSet.add(publicKey.id);
|
|
InputValidator.validatePublicKeyPurposes(publicKey.purposes);
|
|
}
|
|
}
|
|
static validateServices(services) {
|
|
if (services !== undefined && services.length !== 0) {
|
|
const serviceIdSet = new Set();
|
|
for (const service of services) {
|
|
IonRequest.validateService(service);
|
|
if (serviceIdSet.has(service.id)) {
|
|
throw new IonError(ErrorCode.DidDocumentServiceIdDuplicated, 'Service id has to be unique');
|
|
}
|
|
serviceIdSet.add(service.id);
|
|
}
|
|
}
|
|
}
|
|
static validateService(service) {
|
|
InputValidator.validateId(service.id);
|
|
const maxTypeLength = 30;
|
|
if (service.type.length > maxTypeLength) {
|
|
const errorMessage = `Service endpoint type length ${service.type.length} exceeds max allowed length of ${maxTypeLength}.`;
|
|
throw new IonError(ErrorCode.ServiceTypeTooLong, errorMessage);
|
|
}
|
|
// Throw error if `serviceEndpoint` is an array.
|
|
if (Array.isArray(service.serviceEndpoint)) {
|
|
const errorMessage = 'Service endpoint value cannot be an array.';
|
|
throw new IonError(ErrorCode.ServiceEndpointCannotBeAnArray, errorMessage);
|
|
}
|
|
if (typeof service.serviceEndpoint === 'string') {
|
|
const uri = URI.parse(service.serviceEndpoint);
|
|
if (uri.error !== undefined) {
|
|
throw new IonError(ErrorCode.ServiceEndpointStringNotValidUri, `Service endpoint string '${service.serviceEndpoint}' is not a URI.`);
|
|
}
|
|
}
|
|
}
|
|
static validateDeltaSize(delta) {
|
|
const deltaBytes = JsonCanonicalizer.canonicalizeAsBytes(delta);
|
|
if (deltaBytes.length > IonSdkConfig.maxCanonicalizedDeltaSizeInBytes) {
|
|
const errorMessage = `Delta of ${deltaBytes.length} bytes exceeded limit of ${IonSdkConfig.maxCanonicalizedDeltaSizeInBytes} bytes.`;
|
|
throw new IonError(ErrorCode.DeltaExceedsMaximumSize, errorMessage);
|
|
}
|
|
}
|
|
}
|
|
//# sourceMappingURL=IonRequest.js.map
|