diff --git a/src/application/clients/crux-wallet-client.ts b/src/application/clients/crux-wallet-client.ts index 2c1fba37..3f3d40de 100644 --- a/src/application/clients/crux-wallet-client.ts +++ b/src/application/clients/crux-wallet-client.ts @@ -8,6 +8,7 @@ import { IAddress, IAddressMapping, IAssetMatcher, + ICruxDecryptedPrivateInformation, ICruxUserRegistrationStatus, SubdomainRegistrationStatus, SubdomainRegistrationStatusDetail, @@ -233,7 +234,7 @@ export class CruxWalletClient { } @throwCruxClientError - public blacklistUsers = async (fullCruxIDs: string[]): Promise<{success: string[], failures: string[]}> => { + public putBlacklistedUsers = async (fullCruxIDs: string[]): Promise<{success: string[], failures: string[]}> => { await this.initPromise; const cruxUserWithKey = await this.getCruxUserByKey(); if (!cruxUserWithKey) { @@ -249,11 +250,24 @@ export class CruxWalletClient { registeredCruxUsers.push(fullCruxID); } } - cruxUserWithKey.setBlacklistedCruxIDs(registeredCruxUsers); + const decryptedPrivateInfo: ICruxDecryptedPrivateInformation = (await this.keyManager!.symmetricDecrypt!(cruxUserWithKey.privateInformation)) as ICruxDecryptedPrivateInformation; + decryptedPrivateInfo.blacklistedCruxUsers = registeredCruxUsers; + cruxUserWithKey.setPrivateInformation(decryptedPrivateInfo, this.keyManager!); await this.cruxUserRepository.save(cruxUserWithKey, this.getKeyManager()); return {success: registeredCruxUsers, failures}; } + @throwCruxClientError + public getBlacklistedUsers = async (): Promise => { + await this.initPromise; + const cruxUserWithKey = await this.getCruxUserByKey(); + if (!cruxUserWithKey) { + throw ErrorHelper.getPackageError(null, PackageErrorCode.UserDoesNotExist); + } + const decryptedPrivateInfo: ICruxDecryptedPrivateInformation = (await this.keyManager!.symmetricDecrypt!(cruxUserWithKey.privateInformation)) as ICruxDecryptedPrivateInformation; + return decryptedPrivateInfo.blacklistedCruxUsers; + } + // @throwCruxClientError public putPrivateAddressMap = async (fullCruxIDs: string[], newAddressMap: IAddressMapping): Promise => { await this.initPromise; diff --git a/src/core/entities/crux-user.ts b/src/core/entities/crux-user.ts index 5a68f730..a72453e7 100644 --- a/src/core/entities/crux-user.ts +++ b/src/core/entities/crux-user.ts @@ -30,10 +30,15 @@ export interface ICruxUserInformation { export interface ICruxUserData { configuration: ICruxUserConfiguration; privateAddresses: ICruxUserPrivateAddresses; + privateInformation: string; } export interface ICruxUserConfiguration { enabledAssetGroups: string[]; + // blacklistedCruxUsers: string[]; +} + +export interface ICruxDecryptedPrivateInformation { blacklistedCruxUsers: string[]; } @@ -63,6 +68,7 @@ export class CruxUser { private cruxUserConfig!: ICruxUserConfiguration; private cruxUserPrivateAddresses!: ICruxUserPrivateAddresses; private cruxDomain!: CruxDomain; + private cruxPrivateInformation!: string; constructor(cruxUserSubdomain: string, cruxDomain: CruxDomain, addressMap: IAddressMapping, cruxUserInformation: ICruxUserInformation, cruxUserData: ICruxUserData, publicKey?: string) { this.setCruxDomain(cruxDomain); @@ -72,6 +78,7 @@ export class CruxUser { this.setCruxUserConfig(cruxUserData.configuration); this.setPublicKey(publicKey); this.setCruxUserPrivateAddresses(cruxUserData.privateAddresses); + this.setCruxUserPrivateInformation(cruxUserData.privateInformation); log.debug("CruxUser initialised"); } get cruxID() { @@ -92,6 +99,9 @@ export class CruxUser { get privateAddresses() { return this.cruxUserPrivateAddresses; } + get privateInformation() { + return this.cruxPrivateInformation; + } public setSupportedAssetGroups = () => { this.cruxUserConfig.enabledAssetGroups = this.cruxDomain.config.supportedAssetGroups; } @@ -112,8 +122,9 @@ export class CruxUser { throw new BaseError(null, "Not supported by the keyManager in use"); } } - public setBlacklistedCruxIDs = (blacklistedCruxIDs: string[]) => { - this.cruxUserConfig.blacklistedCruxUsers = blacklistedCruxIDs; + public setPrivateInformation = async (decryptedPrivateInfo: ICruxDecryptedPrivateInformation, keyManager: IKeyManager) => { + const encryptedPrivateInfo: string = await keyManager.symmetricEncrypt!(decryptedPrivateInfo); + this.cruxPrivateInformation = encryptedPrivateInfo; } public getAddressFromAsset = async (asset: IGlobalAsset, keyManager?: IKeyManager): Promise => { let address: IAddress|undefined; @@ -178,7 +189,7 @@ export class CruxUser { private setCruxUserConfig = (cruxUserConfiguration: ICruxUserConfiguration) => { // TODO: validation of the configurations this.cruxUserConfig = { - blacklistedCruxUsers: cruxUserConfiguration.blacklistedCruxUsers, + // blacklistedCruxUsers: cruxUserConfiguration.blacklistedCruxUsers, enabledAssetGroups: cruxUserConfiguration.enabledAssetGroups || [], }; } @@ -194,6 +205,9 @@ export class CruxUser { this.cruxUserPrivateAddresses = cruxUserPrivateAddresses; } } + private setCruxUserPrivateInformation = async (privateInfo: string) => { + this.cruxPrivateInformation = privateInfo; + } } export interface IAssetMatcher { diff --git a/src/core/interfaces/key-manager.ts b/src/core/interfaces/key-manager.ts index e4373134..46ba9dd9 100644 --- a/src/core/interfaces/key-manager.ts +++ b/src/core/interfaces/key-manager.ts @@ -3,6 +3,8 @@ export interface IKeyManager { getPubKey: () => Promise; deriveSharedSecret?: (publicKey: string) => Promise; decryptMessage?: (encryptedMessage: string) => Promise; + symmetricEncrypt?: (content: object) => Promise; + symmetricDecrypt?: (encryptedContent: string) => Promise; } export const isInstanceOfKeyManager = (object: any) => { diff --git a/src/infrastructure/implementations/basic-key-manager.ts b/src/infrastructure/implementations/basic-key-manager.ts index 27901749..af98d380 100644 --- a/src/infrastructure/implementations/basic-key-manager.ts +++ b/src/infrastructure/implementations/basic-key-manager.ts @@ -43,6 +43,15 @@ export class BasicKeyManager implements IKeyManager { const decrypted = await ECIESEncryption.decrypt(toDecrypt, privateKey); return decrypted.toString(); } + public symmetricEncrypt = async (content: object): Promise => { + const encrypted = await Encryption.encryptJSON(content, await this.getDecryptedPrivateKey()); + return JSON.stringify(encrypted); + } + public symmetricDecrypt = async (encryptedContent: string): Promise => { + const encryptedContentObj = JSON.parse(encryptedContent); + const decrypted = await Encryption.decryptJSON(encryptedContentObj.encBuffer, encryptedContentObj.iv, await this.getDecryptedPrivateKey()); + return decrypted; + } private init = async (privateKey: string, getEncryptionKey?: () => Promise): Promise => { let encryptionConstant: string; if (getEncryptionKey) { diff --git a/src/infrastructure/implementations/blockstack-crux-user-repository.ts b/src/infrastructure/implementations/blockstack-crux-user-repository.ts index b24086e7..a6023c1b 100644 --- a/src/infrastructure/implementations/blockstack-crux-user-repository.ts +++ b/src/infrastructure/implementations/blockstack-crux-user-repository.ts @@ -1,7 +1,8 @@ import { publicKeyToAddress } from "blockstack"; +import { Encryption } from "src/packages/encryption"; import {CruxDomain} from "../../core/entities/crux-domain"; import { CruxSpec } from "../../core/entities/crux-spec"; -import { CruxUser, IAddressMapping, ICruxUserConfiguration, ICruxUserData, ICruxUserInformation, ICruxUserPrivateAddresses, SubdomainRegistrationStatus } from "../../core/entities/crux-user"; +import { CruxUser, IAddressMapping, ICruxDecryptedPrivateInformation, ICruxUserConfiguration, ICruxUserData, ICruxUserInformation, ICruxUserPrivateAddresses, SubdomainRegistrationStatus } from "../../core/entities/crux-user"; import { ICruxBlockstackInfrastructure } from "../../core/interfaces"; import {ICruxUserRepository, ICruxUserRepositoryOptions} from "../../core/interfaces/crux-user-repository"; import { IKeyManager } from "../../core/interfaces/key-manager"; @@ -57,12 +58,15 @@ export class BlockstackCruxUserRepository implements ICruxUserRepository { } public create = async (cruxIdSubdomain: string, keyManager: IKeyManager): Promise => { // Publishing an empty addressMap while registering the name to be fail safe + const privateInfo = { + blacklistedCruxUsers: [], + }; const cruxUserData: ICruxUserData = { configuration: { - blacklistedCruxUsers: [], enabledAssetGroups: [], }, privateAddresses: {}, + privateInformation: await keyManager.symmetricEncrypt!(privateInfo), }; await this.putCruxpayObject(this.getCruxDomain().id, {}, keyManager); const cruxUserInformation = await this.blockstackService.registerCruxId(this.getCruxIdFromSubdomain(cruxIdSubdomain), this.infrastructure.gaiaHub, keyManager); @@ -79,10 +83,10 @@ export class BlockstackCruxUserRepository implements ICruxUserRepository { let addressMap = {}; let cruxUserData: ICruxUserData = { configuration: { - blacklistedCruxUsers: [], enabledAssetGroups: [], }, privateAddresses: {}, + privateInformation: "", }; let cruxpayPubKey; if (cruxUserInformation.registrationStatus.status === SubdomainRegistrationStatus.DONE) { @@ -107,12 +111,15 @@ export class BlockstackCruxUserRepository implements ICruxUserRepository { return; } let addressMap = {}; + const privateInfo = { + blacklistedCruxUsers: [], + }; let cruxUserData: ICruxUserData = { configuration: { - blacklistedCruxUsers: [], enabledAssetGroups: [], }, privateAddresses: {}, + privateInformation: await keyManager.symmetricEncrypt!(privateInfo), }; let cruxpayPubKey = await keyManager.getPubKey(); if ([SubdomainRegistrationStatus.DONE, SubdomainRegistrationStatus.PENDING].includes(cruxUserInformation.registrationStatus.status)) { @@ -122,13 +129,13 @@ export class BlockstackCruxUserRepository implements ICruxUserRepository { const dereferencedCruxpayObject = this.dereferenceCruxpayObject(cruxpayObject); addressMap = dereferencedCruxpayObject.addressMap; if (dereferencedCruxpayObject.cruxUserData) { - cruxUserData = dereferencedCruxpayObject.cruxUserData; + cruxUserData = Object.assign(cruxUserData, dereferencedCruxpayObject.cruxUserData); } } return new CruxUser(cruxID.components.subdomain, this.getCruxDomain(), addressMap, cruxUserInformation, cruxUserData, cruxpayPubKey); } public save = async (cruxUser: CruxUser, keyManager: IKeyManager): Promise => { - const cruxpayObject = this.constructCruxpayObject(cruxUser.getAddressMap(), cruxUser.info, cruxUser.config, cruxUser.privateAddresses); + const cruxpayObject = this.constructCruxpayObject(cruxUser.getAddressMap(), cruxUser.info, cruxUser.config, cruxUser.privateAddresses, cruxUser.privateInformation); await this.putCruxpayObject(cruxUser.domain.id, cruxpayObject, keyManager); return cruxUser; } @@ -217,11 +224,12 @@ export class BlockstackCruxUserRepository implements ICruxUserRepository { const finalURL = await new GaiaService(gaiaHub, this.cacheStorage).uploadContentToGaiaHub(filename, content, keyManager); return finalURL; } - private constructCruxpayObject = (addressMap: IAddressMapping, userInformation: ICruxUserInformation, userConfiguration: ICruxUserConfiguration, privateAddresses: ICruxUserPrivateAddresses): ICruxpayObject => { + private constructCruxpayObject = (addressMap: IAddressMapping, userInformation: ICruxUserInformation, userConfiguration: ICruxUserConfiguration, privateAddresses: ICruxUserPrivateAddresses, privateInformation: string): ICruxpayObject => { const cruxpayObject: ICruxpayObject = cloneValue(addressMap); cruxpayObject.__userData__ = { configuration: userConfiguration, privateAddresses, + privateInformation, }; return cruxpayObject; }